From e2e1524454d94db9e1b8cf591f337ca0a5fd06da Mon Sep 17 00:00:00 2001 From: ehofman Date: Sun, 4 Oct 2009 13:52:27 +0000 Subject: [PATCH] Initial commit of the new sound system, expect more updates to follow --- simgear/environment/visual_enviro.cxx | 20 +- simgear/environment/visual_enviro.hxx | 6 +- simgear/sound/Makefile.am | 8 +- simgear/sound/jet.wav | Bin 35256 -> 34890 bytes simgear/sound/openal_test2.cxx | 100 +++-- simgear/sound/sample_group.cxx | 443 ++++++++++++++++++++ simgear/sound/sample_group.hxx | 181 +++++++++ simgear/sound/sample_openal.cxx | 561 +++++--------------------- simgear/sound/sample_openal.hxx | 319 +++++++++++---- simgear/sound/soundmgr_openal.cxx | 533 ++++++++++++++---------- simgear/sound/soundmgr_openal.hxx | 220 ++++------ simgear/sound/xmlsound.cxx | 37 +- simgear/sound/xmlsound.hxx | 8 +- simgear/structure/SGAtomic.cxx | 2 +- 14 files changed, 1490 insertions(+), 948 deletions(-) create mode 100644 simgear/sound/sample_group.cxx create mode 100644 simgear/sound/sample_group.hxx diff --git a/simgear/environment/visual_enviro.cxx b/simgear/environment/visual_enviro.cxx index 42b61fd0..71a0c5ea 100644 --- a/simgear/environment/visual_enviro.cxx +++ b/simgear/environment/visual_enviro.cxx @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include #include @@ -175,7 +175,7 @@ SGEnviro::SGEnviro() : lightning_enable_state(false), elapsed_time(0.0), dt(0.0), - soundMgr(NULL), + sampleGroup(NULL), snd_active(false), snd_dist(0.0), min_time_before_lt(0.0), @@ -189,6 +189,8 @@ SGEnviro::SGEnviro() : } SGEnviro::~SGEnviro(void) { + if (sampleGroup) delete sampleGroup; + // OSGFIXME return; list_of_lightning::iterator iLightning; @@ -530,8 +532,8 @@ void SGEnviro::drawRain(double pitch, double roll, double heading, double hspeed } -void SGEnviro::set_soundMgr(SGSoundMgr *mgr) { - soundMgr = mgr; +void SGEnviro::set_sampleGroup(SGSampleGroup *sgr) { + sampleGroup = sgr; } void SGEnviro::drawPrecipitation(double rain_norm, double snow_norm, double hail_norm, double pitch, double roll, double heading, double hspeed) { @@ -616,7 +618,7 @@ void SGLightning::lt_build(void) { top[PY] = alt; top[PZ] = 0; lt_build_tree_branch(0, top, 1.0, 50, top[PY] / 8.0); - if( ! sgEnviro.soundMgr ) + if( ! sgEnviro.sampleGroup ) return; Point3D start( sgEnviro.last_lon*SG_DEGREES_TO_RADIANS, sgEnviro.last_lat*SG_DEGREES_TO_RADIANS, 0.0 ); Point3D dest( lon*SG_DEGREES_TO_RADIANS, lat*SG_DEGREES_TO_RADIANS, 0.0 ); @@ -751,15 +753,15 @@ void SGEnviro::drawLightning(void) { double ax = 0.0, ay = 0.0; ax = cos(course) * dist; ay = sin(course) * dist; - SGSharedPtr snd = soundMgr->find("thunder"); + SGSharedPtr snd = sampleGroup->find("thunder"); if( snd ) { - ALfloat pos[3]={ax, ay, -sgEnviro.last_alt }; - snd->set_source_pos(pos); + SGVec3d pos = SGVec3d(ax, ay, -sgEnviro.last_alt); + snd->set_base_position(pos); snd->play_once(); } } } else { - if( !soundMgr->is_playing("thunder") ) { + if( !sampleGroup->is_playing("thunder") ) { snd_active = false; snd_playing = false; } diff --git a/simgear/environment/visual_enviro.hxx b/simgear/environment/visual_enviro.hxx index 51a76a06..08830c0b 100644 --- a/simgear/environment/visual_enviro.hxx +++ b/simgear/environment/visual_enviro.hxx @@ -32,7 +32,7 @@ using std::vector; using std::string; class SGLightning; -class SGSoundMgr; +class SGSampleGroup; /** * Simulate some echo on a weather radar. @@ -84,7 +84,7 @@ private: sgVec4 fog_color; sgMat4 transform; double last_lon, last_lat, last_alt; - SGSoundMgr *soundMgr; + SGSampleGroup *sampleGroup; bool snd_active, snd_playing; double snd_timer, snd_wait, snd_pos_lat, snd_pos_lon, snd_dist; double min_time_before_lt; @@ -243,7 +243,7 @@ public: * Forward the sound manager instance to be able to play samples. * @param mgr a running sound manager */ - void set_soundMgr(SGSoundMgr *mgr); + void set_sampleGroup(SGSampleGroup *sgr); void setFOV( float w, float h ); void getFOV( float &w, float &h ); diff --git a/simgear/sound/Makefile.am b/simgear/sound/Makefile.am index e421cdef..57f3161a 100644 --- a/simgear/sound/Makefile.am +++ b/simgear/sound/Makefile.am @@ -7,11 +7,13 @@ lib_LIBRARIES = libsgsound.a noinst_HEADERS = include_HEADERS = \ + sample_group.hxx \ sample_openal.hxx \ soundmgr_openal.hxx \ xmlsound.hxx libsgsound_a_SOURCES = \ + sample_group.cxx \ sample_openal.cxx \ soundmgr_openal.cxx \ xmlsound.cxx @@ -27,10 +29,10 @@ libsgsound_a_SOURCES = \ #openal_test2_LDADD = \ # libsgsound.a \ +# $(top_builddir)/simgear/structure/libsgstructure.a \ +# $(top_builddir)/simgear/timing/libsgtiming.a \ # $(top_builddir)/simgear/debug/libsgdebug.a \ # $(top_builddir)/simgear/misc/libsgmisc.a \ -# $(top_builddir)/simgear/structure/libsgstructure.a \ -# $(openal_LIBS) \ -# -lOpenThreads +# $(openal_LIBS) INCLUDES = -I$(top_srcdir) -DSRC_DIR=\"$(top_srcdir)/simgear/sound\" diff --git a/simgear/sound/jet.wav b/simgear/sound/jet.wav index 891c84d0b2064d5e66e594d65e8809b0953c8b07..a06f2afeaebc8fdea8601d67ddb935db8cffef68 100644 GIT binary patch literal 34890 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 literal 35256 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 diff --git a/simgear/sound/openal_test2.cxx b/simgear/sound/openal_test2.cxx index da263620..46a0f621 100644 --- a/simgear/sound/openal_test2.cxx +++ b/simgear/sound/openal_test2.cxx @@ -7,48 +7,94 @@ static unsigned int sleep(unsigned int secs) { return 0; } #include // sleep() #endif -#include "sample_openal.hxx" +#include +#include + #include "soundmgr_openal.hxx" int main( int argc, char *argv[] ) { - SGSoundMgr sm; + SGSampleGroup *sgr; + SGSoundMgr *smgr; + + smgr = new SGSoundMgr; + + smgr->bind(); + smgr->init(); + smgr->set_volume(0.9); + sgr = smgr->find("default", true); - SGSoundSample sample1( SRC_DIR, "jet.wav" ); - sample1.set_volume(0.5); - sample1.set_volume(0.2); - sample1.play_looped(); + SGSoundSample *sample1 = new SGSoundSample( SRC_DIR, "jet.wav" ); + sample1->set_volume(1.0); + sample1->set_pitch(1.0); + sample1->play_looped(); + sgr->add(sample1, "sound1"); + smgr->update(1.0); + printf("playing sample1\n"); sleep(1); - SGSoundSample sample2( SRC_DIR, "jet.wav" ); - sample2.set_volume(0.5); - sample2.set_pitch(0.4); - sample2.play_looped(); + SGSoundSample *sample2 = new SGSoundSample( SRC_DIR, "jet.wav" ); + sample2->set_volume(0.5); + sample2->set_pitch(0.4); + sample2->play_looped(); + sgr->add(sample2, "sound2"); + smgr->update(1.0); + printf("playing sample2\n"); sleep(1); - SGSoundSample sample3( SRC_DIR, "jet.wav" ); - sample3.set_volume(0.5); - sample3.set_pitch(0.8); - sample3.play_looped(); + SGSoundSample *sample3 = new SGSoundSample( SRC_DIR, "jet.wav" ); + sample3->set_volume(0.5); + sample3->set_pitch(0.8); + sample3->play_looped(); + sgr->add(sample3, "sound3"); + smgr->update(1.0); + printf("playing sample3\n"); sleep(1); - SGSoundSample sample4( SRC_DIR, "jet.wav" ); - sample4.set_volume(0.5); - sample4.set_pitch(1.2); - sample4.play_looped(); + SGSoundSample *sample4 = new SGSoundSample( SRC_DIR, "jet.wav" ); + sample4->set_volume(0.5); + sample4->set_pitch(1.2); + sample4->play_looped(); + sgr->add(sample4, "sound4"); + smgr->update(1.0); + printf("playing sample4\n"); sleep(1); - SGSoundSample sample5( SRC_DIR, "jet.wav" ); - sample5.set_volume(0.5); - sample5.set_pitch(1.6); - sample5.play_looped(); + SGSoundSample *sample5 = new SGSoundSample( SRC_DIR, "jet.wav" ); + sample5->set_volume(0.5); + sample5->set_pitch(1.6); + sample5->play_looped(); + sgr->add(sample5, "sound5"); + smgr->update(1.0); + printf("playing sample5\n"); sleep(1); - SGSoundSample sample6( SRC_DIR, "jet.wav" ); - sample6.set_volume(0.5); - sample6.set_pitch(2.0); - sample6.play_looped(); + SGSoundSample *sample6 = new SGSoundSample( SRC_DIR, "jet.wav" ); + sample6->set_volume(0.5); + sample6->set_pitch(2.0); + sample6->play_looped(); + sgr->add(sample6, "sound6"); + smgr->update(1.0); + printf("playing sample6\n"); + sleep(1); + + for (int i=0; i<10; i++) { + sleep(1); + smgr->update(1); + } + + sgr->stop("sound1"); + sgr->stop("sound2"); + sgr->stop("sound3"); + sleep(0.5); + sgr->update(0.5); + sgr->stop("sound4"); + sgr->stop("sound5"); + sgr->stop("sound6"); + sgr->update(1); sleep(1); - sleep(10); + smgr->unbind(); + sleep(2); + delete smgr; } diff --git a/simgear/sound/sample_group.cxx b/simgear/sound/sample_group.cxx new file mode 100644 index 00000000..ffa23e68 --- /dev/null +++ b/simgear/sound/sample_group.cxx @@ -0,0 +1,443 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#if defined (__APPLE__) +# ifdef __GNUC__ +# if ( __GNUC__ >= 3 ) && ( __GNUC_MINOR__ >= 3 ) +// # include +inline int (isnan)(double r) { return !(r <= 0 || r >= 0); } +# else + // any C++ header file undefines isinf and isnan + // so this should be included before + // the functions are STILL in libm (libSystem on mac os x) +extern "C" int isnan (double); +extern "C" int isinf (double); +# endif +# else +// inline int (isinf)(double r) { return isinf(r); } +// inline int (isnan)(double r) { return isnan(r); } +# endif +#endif + +#if defined (__FreeBSD__) +# if __FreeBSD_version < 500000 + extern "C" { + inline int isnan(double r) { return !(r <= 0 || r >= 0); } + } +# endif +#endif + +#if defined (__CYGWIN__) +# include +#endif + +#if defined(__MINGW32__) +# define isnan(x) _isnan(x) +#endif + +#include "soundmgr_openal.hxx" +#include "sample_group.hxx" + +SGSampleGroup::SGSampleGroup () : + _smgr(NULL), + _active(false), + _changed(true), + _position_changed(true), + _position(SGVec3d::zeros().data()), + _orientation(SGVec3f::zeros().data()) +{ + _samples.clear(); +} + +SGSampleGroup::SGSampleGroup ( SGSoundMgr *smgr, const string &refname ) : + _smgr(smgr), + _active(false), + _changed(true), + _position_changed(true), + _position(SGVec3d::zeros().data()), + _orientation(SGVec3f::zeros().data()) +{ + _smgr->add(this, refname); + _active = _smgr->is_working(); + _samples.clear(); +} + +SGSampleGroup::~SGSampleGroup () +{ + _active = false; + + sample_map_iterator sample_current = _samples.begin(); + sample_map_iterator sample_end = _samples.end(); + for ( ; sample_current != sample_end; ++sample_current ) { + SGSoundSample *sample = sample_current->second; + + if ( sample->is_valid_source() && sample->is_playing() ) { + sample->no_valid_source(); + _smgr->release_source( sample->get_source() ); + } + } + + _smgr = 0; +} + +void SGSampleGroup::update( double dt ) { + + if ( !_active ) return; + + // testForALError("start of update!!\n"); + + sample_map_iterator sample_current = _samples.begin(); + sample_map_iterator sample_end = _samples.end(); + for ( ; sample_current != sample_end; ++sample_current ) { + SGSoundSample *sample = sample_current->second; + + if ( !sample->is_valid_source() && sample->is_playing() ) { + // + // 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); + } + + // start playing the sample + ALuint buffer = sample->get_buffer(); + ALuint source = _smgr->request_source(); + if (alIsSource(source) == AL_TRUE && alIsBuffer(buffer) == AL_TRUE) + { + sample->set_source( source ); + + alSourcei( source, AL_BUFFER, buffer ); + testForALError("assign buffer to source"); + + sample->set_source( source ); + update_sample_config( sample ); + + alSourcei( source, AL_SOURCE_RELATIVE, AL_FALSE ); + alSourcei( source, AL_LOOPING, looping ); + alSourcePlay( source ); + testForALError("sample play"); + } else { + if (alIsBuffer(buffer) == AL_FALSE) + SG_LOG( SG_GENERAL, SG_ALERT, "No such buffer!\n"); + // sample->no_valid_source(); + // sadly, no free source available at this time + } + + } else if ( sample->is_valid_source() && sample->has_changed() ) { + if ( !sample->is_playing() ) { + // a request to stop playing the sound has been filed. + + sample->no_valid_source(); + sample->stop(); + _smgr->release_source( sample->get_source() ); + } else { + update_sample_config( sample ); + } + + } else if ( sample->is_valid_source() ) { + // check if the sound has stopped by itself + + unsigned int source = sample->get_source(); + int result; + + alGetSourcei( source, AL_SOURCE_STATE, &result ); + if ( result == AL_STOPPED ) { + // sample is stoped because it wasn't looping + sample->no_valid_source(); + sample->stop(); + _smgr->release_source( source ); + + } + } + testForALError("update"); + } +} + +// add a sound effect, return true if successful +bool SGSampleGroup::add( SGSoundSample *sound, const string& refname ) { + + sample_map_iterator sample_it = _samples.find( refname ); + if ( sample_it != _samples.end() ) { + // sample name already exists + return false; + } + + _samples[refname] = sound; + return true; +} + + +// remove a sound effect, return true if successful +bool SGSampleGroup::remove( const string &refname ) { + + sample_map_iterator sample_it = _samples.find( refname ); + if ( sample_it == _samples.end() ) { + // sample was not found + return false; + } + + _samples.erase( sample_it ); + return true; +} + + +// return true of the specified sound exists in the sound manager system +bool SGSampleGroup::exists( const string &refname ) { + sample_map_iterator sample_it = _samples.find( refname ); + if ( sample_it == _samples.end() ) { + // sample was not found + return false; + } + + return true; +} + + +// return a pointer to the SGSoundSample if the specified sound exists +// in the sound manager system, otherwise return NULL +SGSoundSample *SGSampleGroup::find( const string &refname ) { + sample_map_iterator sample_it = _samples.find( refname ); + if ( sample_it == _samples.end() ) { + // sample was not found + return NULL; + } + + return sample_it->second; +} + + +// stop playing all associated samples +void +SGSampleGroup::suspend () +{ + _active = false; + sample_map_iterator sample_current = _samples.begin(); + sample_map_iterator sample_end = _samples.end(); + for ( ; sample_current != sample_end; ++sample_current ) { + SGSoundSample *sample = sample_current->second; + + if ( sample->is_valid_source() && sample->is_playing() ) { + unsigned int source = sample->get_source(); + alSourcePause( source ); + } + } + testForALError("suspend"); +} + +// resume playing all associated samples +void +SGSampleGroup::resume () +{ + sample_map_iterator sample_current = _samples.begin(); + sample_map_iterator sample_end = _samples.end(); + for ( ; sample_current != sample_end; ++sample_current ) { + SGSoundSample *sample = sample_current->second; + + if ( sample->is_valid_source() && sample->is_playing() ) { + unsigned int source = sample->get_source(); + alSourcePlay( source ); + } + } + testForALError("resume"); + _active = true; +} + + +// tell the scheduler to play the indexed sample in a continuous loop +bool SGSampleGroup::play( const string &refname, bool looping = false ) { + SGSoundSample *sample = find( refname ); + + if ( sample == NULL ) { + return false; + } + + sample->play( looping ); + return true; +} + + +// return true of the specified sound is currently being played +bool SGSampleGroup::is_playing( const string& refname ) { + SGSoundSample *sample = find( refname ); + + if ( sample == NULL ) { + return false; + } + + return ( sample->is_playing() ) ? true : false; +} + +// immediate stop playing the sound +bool SGSampleGroup::stop( const string& refname ) { + SGSoundSample *sample = find( refname ); + + if ( sample == NULL ) { + return false; + } + + sample->stop(); + return true; +} + + +// set source position of all managed sounds +void SGSampleGroup::set_position( SGVec3d pos ) { + if ( isnan(pos.data()[0]) || isnan(pos.data()[1]) || isnan(pos.data()[2]) ) + { + SG_LOG( SG_GENERAL, SG_ALERT, "NAN's found in SampleGroup postion"); + return; + } + + sample_map_iterator sample_current = _samples.begin(); + sample_map_iterator sample_end = _samples.end(); + for ( ; sample_current != sample_end; ++sample_current ) { + SGSoundSample *sample = sample_current->second; + sample->set_base_position( pos ); + } +} + +// set source velocity of all managed sounds +void SGSampleGroup::set_velocity( SGVec3f vel ) { + if ( isnan(vel.data()[0]) || isnan(vel.data()[1]) || isnan(vel.data()[2]) ) + { + SG_LOG( SG_GENERAL, SG_ALERT, "NAN's found in SampleGroup velocity"); + return; + } + + sample_map_iterator sample_current = _samples.begin(); + sample_map_iterator sample_end = _samples.end(); + for ( ; sample_current != sample_end; ++sample_current ) { + SGSoundSample *sample = sample_current->second; + sample->set_velocity( vel ); + } +} + +// ste the source orientation of all managed sounds +void SGSampleGroup::set_orientation( SGVec3f ori ) { + if ( isnan(ori.data()[0]) || isnan(ori.data()[1]) || isnan(ori.data()[2]) ) + { + SG_LOG( SG_GENERAL, SG_ALERT, "NAN's found in SampleGroup orientation"); + return; + } + + sample_map_iterator sample_current = _samples.begin(); + sample_map_iterator sample_end = _samples.end(); + for ( ; sample_current != sample_end; ++sample_current ) { + SGSoundSample *sample = sample_current->second; + sample->set_orientation( ori ); + } +} + +void SGSampleGroup::update_sample_config( SGSoundSample *sample ) { + if ( sample->is_valid_source() ) { + unsigned int source = sample->get_source(); + + alSourcefv( source, AL_POSITION, sample->get_position()); + alSourcefv( source, AL_DIRECTION, sample->get_direction() ); + alSourcefv( source, AL_VELOCITY, sample->get_velocity() ); + testForALError("position and orientation"); + + alSourcef( source, AL_PITCH, sample->get_pitch() ); + alSourcef( source, AL_GAIN, sample->get_volume() ); + testForALError("pitch and gain"); + + if ( sample->has_static_data_changed() ) { + alSourcef( source, AL_CONE_INNER_ANGLE, sample->get_innerangle() ); + alSourcef( source, AL_CONE_OUTER_ANGLE, sample->get_outerangle() ); + alSourcef( source, AL_CONE_OUTER_GAIN, sample->get_outergain() ); + testForALError("audio cone"); + + alSourcef( source, AL_ROLLOFF_FACTOR, 1.0 ); + alSourcef( source, AL_MAX_DISTANCE, sample->get_max_dist() ); + alSourcef( source, AL_REFERENCE_DISTANCE, + sample->get_reference_dist() ); + testForALError("distance rolloff"); + } + } +} + +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; + if (_volume < 0.0) _volume = 0.0; + if (_volume > 1.0) _volume = 1.0; + + sample_map_iterator sample_current = _samples.begin(); + sample_map_iterator sample_end = _samples.end(); + for ( ; sample_current != sample_end; ++sample_current ) { + SGSoundSample *sample = sample_current->second; + sample->set_master_volume( _volume ); + } +} + +bool SGSampleGroup::testForError(void *p, string s) +{ + if (p == NULL) { + SG_LOG( SG_GENERAL, SG_ALERT, "Error (sample group): " << s); + return true; + } + return false; +} + +bool SGSampleGroup::testForALError(string s) +{ + ALenum error = alGetError(); + if (error != AL_NO_ERROR) { + SG_LOG( SG_GENERAL, SG_ALERT, "AL Error (sample group): " + << alGetString(error) << " at " << s); + return true; + } + return false; +} + diff --git a/simgear/sound/sample_group.hxx b/simgear/sound/sample_group.hxx new file mode 100644 index 00000000..3e277014 --- /dev/null +++ b/simgear/sound/sample_group.hxx @@ -0,0 +1,181 @@ +// soundmgr.hxx -- Sound effect management class +// +// Sampel Group handler initially written by Erik Hofman +// +// Copyright (C) 2009 Erik Hofman - +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// +// $Id$ + +/** + * \file sample_group.hxx + * sample groups contain all sounds related to one specific object and + * have to be added to the sound manager, otherwise they won't get processed. + */ + +#ifndef _SG_SAMPLE_GROUP_OPENAL_HXX +#define _SG_SAMPLE_GROUP_OPENAL_HXX 1 + +#ifndef __cplusplus +# error This library requires C++ +#endif + +#if defined(__APPLE__) +# include +#else +# include +#endif + +#include +#include + +#include +#include +#include +#include +#include + +#include "sample_openal.hxx" + +using std::map; +using std::string; + +typedef map < string, SGSharedPtr > sample_map; +typedef sample_map::iterator sample_map_iterator; +typedef sample_map::const_iterator const_sample_map_iterator; + +class SGSoundMgr; + +class SGSampleGroup : public SGReferenced +{ +public: + SGSampleGroup (); + SGSampleGroup ( SGSoundMgr *smgr, const string &refname ); + ~SGSampleGroup (); + + virtual void update (double dt); + + /** + * add a sound effect, return true if successful + */ + bool add( SGSoundSample *sound, const string& refname ); + + /** + * remove a sound effect, return true if successful + */ + bool remove( const string& refname ); + + /** + * return true of the specified sound exists in the sound manager system + */ + bool exists( const string& refname ); + + /** + * return a pointer to the SGSoundSample if the specified sound + * exists in the sound manager system, otherwise return NULL + */ + SGSoundSample *find( const string& refname ); + + /** + * request to stop playing all associated samples until further notice + */ + void suspend(); + + /** + * request to resume playing all associated samples + */ + void resume(); + + + /** + * request to start playing the associated samples + */ + bool play( const string& refname, bool looping ); + + /** + * tell the scheduler to play the indexed sample in a continuous + * loop + */ + inline bool play_looped( const string& refname ) { + return play( refname, true ); + } + + /** + * tell the scheduler to play the indexed sample once + */ + inline bool play_once( const string& refname ) { + return play( refname, false ); + } + + /** + * return true of the specified sound is currently being played + */ + bool is_playing( const string& refname ); + + /** + * request to stop playing the associated samples + */ + bool stop( const string& refname ); + + /** + * set overall volume for the application. + * @param must be between 0.0 and 1.0 + */ + void set_volume( float vol ); + + /** + * set the positions of all managed sound sources + */ + void set_position( SGVec3d pos ); + + /** + * set the velocities of all managed sound sources + */ + void set_velocity( SGVec3f vel ); + + /** + * set the orientation of all managed sound sources + */ + void set_orientation( SGVec3f ori ); + + /** + * load the data of the sound sample + */ + void load_file(SGSoundSample *sound); + +protected: + SGSoundMgr *_smgr; + bool _active; + +private: + bool _changed; + bool _position_changed; + + float _volume; + + SGVec3d _position; + SGVec3f _orientation; + + sample_map _samples; + + bool testForALError(string s); + bool testForError(void *p, string s); + + void update_sample_config( SGSoundSample *sound ); +}; + +#endif // _SG_SAMPLE_GROUP_OPENAL_HXX + diff --git a/simgear/sound/sample_openal.cxx b/simgear/sound/sample_openal.cxx index 52eaea18..917f0596 100644 --- a/simgear/sound/sample_openal.cxx +++ b/simgear/sound/sample_openal.cxx @@ -15,8 +15,8 @@ // General Public License for more details. // // You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // $Id$ @@ -24,20 +24,12 @@ # include #endif -#if defined( __APPLE__ ) -# define AL_ILLEGAL_ENUM AL_INVALID_ENUM -# define AL_ILLEGAL_COMMAND AL_INVALID_OPERATION -# include -# include -#else -# include -# include -#endif - #include -#include #include +#include +#include +#include "soundmgr_openal.hxx" #include "sample_openal.hxx" @@ -45,477 +37,140 @@ // SGSoundSample // - -static bool print_openal_error(const string &s = "unknown") { - ALuint error = alGetError(); - if ( error == AL_NO_ERROR ) { - return false; - } else if ( error == AL_INVALID_NAME ) { - SG_LOG( SG_GENERAL, SG_ALERT, "OpenAL error (AL_INVALID_NAME): " << s ); - } else if ( error == AL_ILLEGAL_ENUM ) { - SG_LOG( SG_GENERAL, SG_ALERT, "OpenAL error (AL_ILLEGAL_ENUM): " << s ); - } else if ( error == AL_INVALID_VALUE ) { - SG_LOG( SG_GENERAL, SG_ALERT, "OpenAL error (AL_INVALID_VALUE): " << s ); - } else if ( error == AL_ILLEGAL_COMMAND ) { - SG_LOG( SG_GENERAL, SG_ALERT, "OpenAL error (AL_ILLEGAL_COMMAND): " << s ); - } else if ( error == AL_OUT_OF_MEMORY ) { - SG_LOG( SG_GENERAL, SG_ALERT, "OpenAL error (AL_OUT_OF_MEMORY): " << s ); - } else { - SG_LOG( SG_GENERAL, SG_ALERT, "Unhandled error code = " << error ); - } - return error != 0; -} - // empty constructor SGSoundSample::SGSoundSample() : - buffer(0), - source(0), - pitch(1.0), - volume(1.0), - reference_dist(500.0), - max_dist(3000.), - loop(AL_FALSE), -#ifdef USE_SOFTWARE_DOPPLER - doppler_pitch_factor(1), - doppler_volume_factor(1), -#endif - playing(false), - no_Doppler_effect(true) + _absolute_pos(SGVec3d::zeros().data()), + _relative_pos(SGVec3f::zeros().data()), + _base_pos(SGVec3d::zeros().data()), + _direction(SGVec3f::zeros().data()), + _velocity(SGVec3f::zeros().data()), + _sample_name(""), + _data(NULL), + _format(AL_FORMAT_MONO8), + _size(0), + _freq(0), + _valid_buffer(false), + _buffer(SGSoundMgr::NO_BUFFER), + _valid_source(false), + _source(SGSoundMgr::NO_SOURCE), + _inner_angle(360.0), + _outer_angle(360.0), + _outer_gain(0.0), + _pitch(1.0), + _volume(1.0), + _master_volume(1.0), + _reference_dist(500.0), + _max_dist(3000.0), + _loop(AL_FALSE), + _playing(false), + _changed(true), + _static_changed(true), + _is_file(false) { } // constructor -SGSoundSample::SGSoundSample( const char *path, const char *file, bool _no_Doppler_effect ) : - buffer(0), - source(0), - pitch(1.0), - volume(1.0), - reference_dist(500.0), - max_dist(3000.), - loop(AL_FALSE), -#ifdef USE_SOFTWARE_DOPPLER - doppler_pitch_factor(1), - doppler_volume_factor(1), -#endif - playing(false), - no_Doppler_effect(_no_Doppler_effect) +SGSoundSample::SGSoundSample( const char *path, const char *file ) : + _absolute_pos(SGVec3d::zeros().data()), + _relative_pos(SGVec3f::zeros().data()), + _base_pos(SGVec3d::zeros().data()), + _direction(SGVec3f::zeros().data()), + _velocity(SGVec3f::zeros().data()), + _format(AL_FORMAT_MONO8), + _size(0), + _freq(0), + _valid_buffer(false), + _buffer(SGSoundMgr::NO_BUFFER), + _valid_source(false), + _source(SGSoundMgr::NO_SOURCE), + _inner_angle(360.0), + _outer_angle(360.0), + _outer_gain(0.0), + _pitch(1.0), + _volume(1.0), + _master_volume(1.0), + _reference_dist(500.0), + _max_dist(3000.0), + _loop(AL_FALSE), + _playing(false), + _changed(true), + _static_changed(true), + _is_file(true) { SGPath samplepath( path ); if ( strlen(file) ) { samplepath.append( file ); } - sample_name = samplepath.str(); + _sample_name = samplepath.str(); - SG_LOG( SG_GENERAL, SG_DEBUG, "From file sounds sample = " + SG_LOG( SG_GENERAL, SG_DEBUG, "From file sounds sample = " << samplepath.str() ); - - source_pos[0] = 0.0; source_pos[1] = 0.0; source_pos[2] = 0.0; - offset_pos[0] = 0.0; offset_pos[1] = 0.0; offset_pos[2] = 0.0; - source_vel[0] = 0.0; source_vel[1] = 0.0; source_vel[2] = 0.0; - direction[0] = 0.0; direction[1] = 0.0; direction[2] = 0.0; - inner = outer = 360.0; outergain = 0.0; - - // clear errors from elsewhere? - alGetError(); - - // create an OpenAL buffer handle - alGenBuffers(1, &buffer); - if ( print_openal_error("constructor (alGenBuffers)") ) { - throw sg_exception("Failed to gen OpenAL buffer."); - } - - // Load the sample file -#if defined(ALUT_API_MAJOR_VERSION) && ALUT_API_MAJOR_VERSION >= 1 - - buffer = alutCreateBufferFromFile(samplepath.c_str()); - if (buffer == AL_NONE) { - ALenum error = alutGetError (); - print_openal_error("constructor (alutCreateBufferFromFile)"); - throw sg_io_exception("Failed to load wav file: ", - sg_location(string(alutGetErrorString (error)))); - } - -#else - // - // pre 1.0 alut version - // - ALvoid* data = load_file(path, file); - - // Copy data to the internal OpenAL buffer - alBufferData( buffer, format, data, size, freq ); - - if ( print_openal_error("constructor (alBufferData)") ) { - SG_LOG( SG_GENERAL, SG_ALERT, "Trying to use file " << file ); - throw sg_exception("Failed to buffer data."); - } - - alutUnloadWAV( format, data, size, freq ); -#endif - - print_openal_error("constructor return"); } // constructor -SGSoundSample::SGSoundSample( unsigned char *_data, int len, int _freq, bool _no_Doppler_effect ) : - buffer(0), - source(0), - pitch(1.0), - volume(1.0), - reference_dist(500.0), - max_dist(3000.), - loop(AL_FALSE), -#ifdef USE_SOFTWARE_DOPPLER - doppler_pitch_factor(1), - doppler_volume_factor(1), -#endif - playing(false), - no_Doppler_effect(_no_Doppler_effect) +SGSoundSample::SGSoundSample( unsigned char *data, int len, int freq, int format ) : + _absolute_pos(SGVec3d::zeros().data()), + _relative_pos(SGVec3f::zeros().data()), + _base_pos(SGVec3d::zeros().data()), + _direction(SGVec3f::zeros().data()), + _velocity(SGVec3f::zeros().data()), + _data(data), + _format(format), + _size(len), + _freq(freq), + _valid_buffer(false), + _buffer(SGSoundMgr::NO_BUFFER), + _valid_source(false), + _source(SGSoundMgr::NO_SOURCE), + _inner_angle(360.0), + _outer_angle(360.0), + _outer_gain(0.0), + _pitch(1.0), + _volume(1.0), + _master_volume(1.0), + _reference_dist(500.0), + _max_dist(3000.0), + _loop(AL_FALSE), + _playing(false), + _changed(true), + _static_changed(true), + _is_file(false) { + _sample_name = "unknown, data supplied by caller"; SG_LOG( SG_GENERAL, SG_DEBUG, "In memory sounds sample" ); - - sample_name = "unknown, generated from data"; - - source_pos[0] = 0.0; source_pos[1] = 0.0; source_pos[2] = 0.0; - offset_pos[0] = 0.0; offset_pos[1] = 0.0; offset_pos[2] = 0.0; - source_vel[0] = 0.0; source_vel[1] = 0.0; source_vel[2] = 0.0; - direction[0] = 0.0; direction[1] = 0.0; direction[2] = 0.0; - inner = outer = 360.0; outergain = 0.0; - - // clear errors from elsewhere? - alGetError(); - - // Load wav data into a buffer. - alGenBuffers(1, &buffer); - if ( print_openal_error("constructor (alGenBuffers)") ) { - throw sg_exception("Failed to gen buffer." ); - } - - format = AL_FORMAT_MONO8; - size = len; - freq = _freq; - - alBufferData( buffer, format, _data, size, freq ); - if ( print_openal_error("constructor (alBufferData)") ) { - throw sg_exception("Failed to buffer data."); - } - - print_openal_error("constructor return"); } // destructor SGSoundSample::~SGSoundSample() { - SG_LOG( SG_GENERAL, SG_INFO, "Deleting a sample" ); - if (buffer) - alDeleteBuffers(1, &buffer); -} - - -// play the sample -void SGSoundSample::play( bool _loop ) { - - if ( source ) { - alSourceStop( source ); - } - - playing = bind_source(); - if ( playing ) { - loop = _loop; - - alSourcei( source, AL_LOOPING, loop ); - alSourcePlay( source ); - - print_openal_error("play (alSourcePlay)"); +#if 0 + if (_data != NULL) { + delete[] _data; + _data = NULL; } -} - - -// stop playing the sample -void SGSoundSample::stop() { - if (playing) { - alSourceStop( source ); - alDeleteSources(1, &source); - source = 0; - print_openal_error("stop (alDeleteSources)"); - } - playing = false; -} - -// Generate sound source -bool -SGSoundSample::bind_source() { - - if ( playing ) { - return true; - } - if ( buffer == 0 ) { - return false; - } - - // Bind buffer with a source. - alGetError(); - alGenSources(1, &source); - if ( print_openal_error("bind_source (alGenSources)") ) { - // No biggy, better luck next time. - SG_LOG( SG_GENERAL, SG_ALERT, "Failed to generate audio source."); - // SG_LOG( SG_GENERAL, SG_ALERT, "Please update your sound driver and try again."); - return false; - } - - alSourcei( source, AL_BUFFER, buffer ); -#ifndef USE_SOFTWARE_DOPPLER - alSourcef( source, AL_PITCH, pitch ); - alSourcef( source, AL_GAIN, volume ); -#else - print_openal_error("bind_sources return"); - alSourcef( source, AL_PITCH, pitch * doppler_pitch_factor ); - alGetError(); // ignore if the pitch is clamped by the driver - alSourcef( source, AL_GAIN, volume * doppler_volume_factor ); -#endif - alSourcefv( source, AL_POSITION, source_pos ); - alSourcefv( source, AL_DIRECTION, direction ); - alSourcef( source, AL_CONE_INNER_ANGLE, inner ); - alSourcef( source, AL_CONE_OUTER_ANGLE, outer ); - alSourcef( source, AL_CONE_OUTER_GAIN, outergain); -#ifdef USE_OPEN_AL_DOPPLER - alSourcefv( source, AL_VELOCITY, source_vel ); #endif - alSourcei( source, AL_LOOPING, loop ); - - alSourcei( source, AL_SOURCE_RELATIVE, AL_TRUE ); - alSourcef( source, AL_REFERENCE_DISTANCE, reference_dist ); - alSourcef( source, AL_MAX_DISTANCE, max_dist ); - - print_openal_error("bind_sources return"); - - return true; -} - -void -SGSoundSample::set_pitch( double p ) { - // clamp in the range of 0.01 to 2.0 - if ( p < 0.01 ) { p = 0.01; } - if ( p > 2.0 ) { p = 2.0; } - pitch = p; - if (playing) { -#ifndef USE_SOFTWARE_DOPPLER - alSourcef( source, AL_PITCH, pitch ); - print_openal_error("set_pitch"); -#else - alSourcef( source, AL_PITCH, pitch * doppler_pitch_factor ); - alGetError(); // ignore if the pitch is clamped by the driver -#endif - } -} - -void -SGSoundSample::set_volume( double v ) { - volume = v; - if (playing) { -#ifndef USE_SOFTWARE_DOPPLER - alSourcef( source, AL_GAIN, volume ); -#else - alSourcef( source, AL_GAIN, volume * doppler_volume_factor ); -#endif - print_openal_error("set_volume"); - } -} - - -bool -SGSoundSample::is_playing( ) { - if (playing) { - ALint result; - alGetSourcei( source, AL_SOURCE_STATE, &result ); - if ( alGetError() != AL_NO_ERROR) { - SG_LOG( SG_GENERAL, SG_ALERT, - "Oops AL error in sample is_playing(): " << sample_name ); - } - return (result == AL_PLAYING) ; - } else - return false; -} - -void -SGSoundSample::set_source_pos( ALfloat *pos ) { - source_pos[0] = pos[0]; - source_pos[1] = pos[1]; - source_pos[2] = pos[2]; - - if (playing) { - sgVec3 final_pos; - sgAddVec3( final_pos, source_pos, offset_pos ); - - alSourcefv( source, AL_POSITION, final_pos ); - print_openal_error("set_source_pos"); - } -} - -void -SGSoundSample::set_offset_pos( ALfloat *pos ) { - offset_pos[0] = pos[0]; - offset_pos[1] = pos[1]; - offset_pos[2] = pos[2]; - - if (playing) { - sgVec3 final_pos; - sgAddVec3( final_pos, source_pos, offset_pos ); - - alSourcefv( source, AL_POSITION, final_pos ); - print_openal_error("set_offset_pos"); - } -} - -void -SGSoundSample::set_orientation( ALfloat *dir, ALfloat inner_angle, - ALfloat outer_angle, - ALfloat outer_gain) -{ - inner = inner_angle; - outer = outer_angle; - outergain = outer_gain; - direction[0] = dir[0]; - direction[1] = dir[1]; - direction[2] = dir[2]; - if (playing) { - alSourcefv( source, AL_DIRECTION, dir); - alSourcef( source, AL_CONE_INNER_ANGLE, inner ); - alSourcef( source, AL_CONE_OUTER_ANGLE, outer ); - alSourcef( source, AL_CONE_OUTER_GAIN, outergain ); - } } -void -SGSoundSample::set_source_vel( ALfloat *vel, ALfloat *listener_vel ) { - if (no_Doppler_effect) { - source_vel[0] = listener_vel[0]; - source_vel[1] = listener_vel[1]; - source_vel[2] = listener_vel[2]; - } else { - source_vel[0] = vel[0]; - source_vel[1] = vel[1]; - source_vel[2] = vel[2]; - } -#ifdef USE_OPEN_AL_DOPPLER - if (playing) { - alSourcefv( source, AL_VELOCITY, source_vel ); - } -#elif defined (USE_OPEN_AL_DOPPLER_WITH_FIXED_LISTENER) - if (playing) { - sgVec3 relative_vel; - sgSubVec3( relative_vel, source_vel, listener_vel ); - alSourcefv( source, AL_VELOCITY, relative_vel ); - } -#else - if (no_Doppler_effect) { - doppler_pitch_factor = 1; - doppler_volume_factor = 1; - return; - } - double doppler, mfp; - sgVec3 final_pos; - sgAddVec3( final_pos, source_pos, offset_pos ); - mfp = sgLengthVec3(final_pos); - if (mfp > 1e-6) { - double vls = -sgScalarProductVec3( listener_vel, final_pos ) / mfp; - double vss = -sgScalarProductVec3( source_vel, final_pos ) / mfp; - if (fabs(340 - vss) > 1e-6) - { - doppler = (340 - vls) / (340 - vss); - doppler = ( doppler > 0) ? ( ( doppler < 10) ? doppler : 10 ) : 0; - } - else - doppler = 0; - } - else - doppler = 1; - /* the OpenAL documentation of the Doppler calculation - SS: AL_SPEED_OF_SOUND = speed of sound (default value 343.3) - DF: AL_DOPPLER_FACTOR = Doppler factor (default 1.0) - vls: Listener velocity scalar (scalar, projected on source-to-listener vector) - vss: Source velocity scalar (scalar, projected on source-to-listener vector) - SL = source to listener vector - SV = Source Velocity vector - LV = Listener Velocity vector - vls = DotProduct(SL, LV) / Mag(SL) - vss = DotProduct(SL, SV) / Mag(SL) - Dopper Calculation: - vss = min(vss, SS/DF) - vls = min(vls, SS/DF) - f' = f * (SS - DF*vls) / (SS - DF*vss) - */ - if (doppler > 0.1) { - if (doppler < 10) { - doppler_pitch_factor = doppler; - doppler_volume_factor = 1; - } - else { - doppler_pitch_factor = (doppler < 11) ? doppler : 11; - doppler_volume_factor = (doppler < 11) ? 11-doppler : 0; - } - } - else { - doppler_pitch_factor = 0.1; - doppler_volume_factor = (doppler > 0) ? doppler * 10 : 0; - } - if (playing) { - alSourcef( source, AL_GAIN, volume * doppler_volume_factor ); - print_openal_error("set_source_vel: volume"); - alSourcef( source, AL_PITCH, pitch * doppler_pitch_factor ); - alGetError(); //ignore if the pitch is clamped - } -#endif +void SGSoundSample::set_base_position( SGVec3d pos ) { + _base_pos = pos; + update_absolute_position(); + _changed = true; } -void -SGSoundSample::set_reference_dist( ALfloat dist ) { - reference_dist = dist; - if (playing) { - alSourcef( source, AL_REFERENCE_DISTANCE, reference_dist ); - } +void SGSoundSample::set_relative_position( SGVec3f pos ) { + _relative_pos = pos; + update_absolute_position(); + _changed = true; } - -void -SGSoundSample::set_max_dist( ALfloat dist ) { - max_dist = dist; - if (playing) { - alSourcef( source, AL_MAX_DISTANCE, max_dist ); - } +void SGSoundSample::set_orientation( SGVec3f dir ) { + _direction = dir; + update_absolute_position(); + _changed = true; } -ALvoid * -SGSoundSample::load_file(const char *path, const char *file) -{ - ALvoid* data = 0; - - SGPath samplepath( path ); - if ( strlen(file) ) { - samplepath.append( file ); - } - -#if defined(ALUT_API_MAJOR_VERSION) && ALUT_API_MAJOR_VERSION >= 1 - ALfloat freqf; - data = alutLoadMemoryFromFile(samplepath.c_str(), &format, &size, &freqf ); - if (data == NULL) { - throw sg_io_exception("Failed to load wav file.", - sg_location(samplepath.str())); - } - freq = (ALsizei)freqf; -#else -# if defined (__APPLE__) - alutLoadWAVFile( (ALbyte *)samplepath.c_str(), - &format, &data, &size, &freq ); -# else - alutLoadWAVFile( (ALbyte *)samplepath.c_str(), - &format, &data, &size, &freq, &loop ); -# endif - if ( print_openal_error("constructor (alutLoadWAVFile)") ) { - throw sg_io_exception("Failed to load wav file.", - sg_location(samplepath.str())); - } -#endif - - return data; +void SGSoundSample::update_absolute_position() { + SGQuatf orient = SGQuatf::fromAngleAxis(_direction); + SGVec3f modified_relative_pos = orient.transform(_relative_pos); + _absolute_pos = _base_pos + toVec3d(modified_relative_pos); } - diff --git a/simgear/sound/sample_openal.hxx b/simgear/sound/sample_openal.hxx index 1131981b..5d4baf62 100644 --- a/simgear/sound/sample_openal.hxx +++ b/simgear/sound/sample_openal.hxx @@ -32,37 +32,16 @@ # error This library requires C++ #endif -#include - #include +#include #include #include #include +#include #include -#if defined(__APPLE__) -# define AL_ILLEGAL_ENUM AL_INVALID_ENUM -# define AL_ILLEGAL_COMMAND AL_INVALID_OPERATION -# include -# include -#else -# include -# include -#endif - -#ifndef HAVE_WINDOWS_H - #ifdef AL_VERSION_1_2 - #define USE_OPEN_AL_DOPPLER should work - #else - #define USE_OPEN_AL_DOPPLER_WITH_FIXED_LISTENER better than nothing - #endif -#else - // the Open_AL Doppler calculation seems to be buggy on windows - #define USE_SOFTWARE_DOPPLER seem to be necessary -#endif - using std::string; /** @@ -73,45 +52,51 @@ class SGSoundSample : public SGReferenced { private: - string sample_name; + // Position of the source sound. + SGVec3d _absolute_pos; // absolute position + SGVec3f _relative_pos; // position relative to the base position + SGVec3d _base_pos; // base position - // Buffers hold sound data. - ALuint buffer; + // The orientation of the sound (direction and cut-off angles) + SGVec3f _direction; - // Sources are points emitting sound. - ALuint source; + // Velocity of the source sound. + SGVec3f _velocity; - // Position of the source sound. - ALfloat source_pos[3]; + string _sample_name; + unsigned char *_data; + + // configuration values + int _format; + int _size; + int _freq; - // A constant offset to be applied to the final source_pos - ALfloat offset_pos[3]; + // Buffers hold sound data. + bool _valid_buffer; + unsigned int _buffer; + + // Sources are points emitting sound. + bool _valid_source; + unsigned int _source; // The orientation of the sound (direction and cut-off angles) - ALfloat direction[3]; - ALfloat inner, outer, outergain; + float _inner_angle; + float _outer_angle; + float _outer_gain; - // Velocity of the source sound. - ALfloat source_vel[3]; + float _pitch; + float _volume; + float _master_volume; + float _reference_dist; + float _max_dist; + bool _loop; - // configuration values - ALenum format; - ALsizei size; - ALsizei freq; - - double pitch; - double volume; -#ifdef USE_SOFTWARE_DOPPLER - double doppler_pitch_factor; - double doppler_volume_factor; -#endif - double reference_dist; - double max_dist; - ALboolean loop; + bool _playing; + bool _changed; + bool _static_changed; + bool _is_file; - bool playing; - bool bind_source(); - bool no_Doppler_effect; + void update_absolute_position(); public: @@ -128,7 +113,7 @@ public: should usually be true unless you want to manipulate the data later.) */ - SGSoundSample( const char *path, const char *file, bool no_Doppler_effect = true ); + SGSoundSample( const char *path, const char *file ); /** * Constructor. @@ -139,23 +124,50 @@ public: should usually be true unless you want to manipulate the data later.) */ - SGSoundSample( unsigned char *_data, int len, int _freq, bool no_Doppler_effect = true ); + SGSoundSample( unsigned char *data, int len, int freq, int format = AL_FORMAT_MONO8 ); + + ~SGSoundSample (); + + /** + * detect wheter the sample holds the information of a sound file + */ + inline bool is_file() const { return _is_file; } + + /** + * Test whether this sample has a changed configuration since the last + * call. (Calling this function resets the value). + */ + inline bool has_changed() { + bool b = _changed; _changed = false; return b; + } + + inline bool has_static_data_changed() { + bool b = _static_changed; _static_changed = false; return b; + } - ~SGSoundSample(); /** * Start playing this sample. * * @param _loop Define whether the sound should be played in a loop. */ - void play( bool _loop ); + inline void play( bool loop ) { + _playing = true; _loop = loop; _changed = true; + } + + /** + * Return if the sample is looping or not. + */ + inline bool get_looping() { return _loop; } /** * Stop playing this sample. * * @param sched A pointer to the appropriate scheduler. */ - void stop(); + inline void stop() { + _playing = false; _changed = true; + } /** * Play this sample once. @@ -173,77 +185,226 @@ public: * Test if a sample is currently playing. * @return true if is is playing, false otherwise. */ - bool is_playing( ); + inline bool is_playing() { return _playing; } + + /** + * set the data associated with this sample + */ + inline void set_data( unsigned char* data ) { + _data = data; + } + + /** + * @return the data associated with this sample + */ + inline void* get_data() const { return _data; } + + /** + * free the data associated with this sample + */ + inline void free_data() { + if (_data != NULL) { delete[] _data; _data = NULL; } + } + + /** + * set the source id of this source + */ + inline void set_source(unsigned int s) { + _source = s; _valid_source = true; _changed = true; + } + + /** + * get the source id of this source + */ + inline unsigned int get_source() { return _source; } + + /** + * detect wheter the source id of the sample is valid + */ + inline bool is_valid_source() const { return _valid_source; } + + /** + * set the source id of the sample to invalid. + */ + inline void no_valid_source() { + _valid_source = false; + } + + /** + * set the buffer id of this source + */ + inline void set_buffer(unsigned int b) { + _buffer = b; _valid_buffer = true; _changed = true; + } + + /** + * get the buffer id of this source + */ + inline unsigned int get_buffer() { return _buffer; } + + /** + * detect wheter the source id of the sample is valid + */ + inline bool is_valid_buffer() const { return _valid_buffer; } + + /** + * set the source id of the sample to invalid. + */ + inline void no_valid_buffer() { + _valid_buffer = false; + } /** * Get the current pitch setting of this sample. */ - inline double get_pitch() const { return pitch; } + inline float get_pitch() { return _pitch; } /** * Set the pitch of this sample. */ - void set_pitch( double p ); + inline void set_pitch( float p ) { + _pitch = p; _changed = true; + } /** * Get the current volume setting of this sample. */ - inline double get_volume() const { return volume; } + inline float get_volume() { return _volume * _master_volume; } + + /** + * Set the master (sampel group) volume of this sample. + */ + inline void set_master_volume( float v ) { + _master_volume = v; _changed = true; + } /** * Set the volume of this sample. */ - void set_volume( double v ); + inline void set_volume( float v ) { + _volume = v; _changed = true; + } + + /** + * Set the format of the sounds sample + */ + inline void set_format( int format ) { + _format = format; + } + + /** + * Returns the format of the sounds sample + */ + inline int get_format() { return _format; } + + + /** + * Set the frequency of the sounds sample + */ + inline void set_frequency( int freq ) { + _freq = freq; _changed = true; + } + + /** + * Returns the frequency of the sounds sample + */ + inline int get_frequency() { return _freq; } /** * Returns the size of the sounds sample */ - inline int get_size() { - return size; + inline void set_size( int size ) { + _size = size; } /** - * Set position of sound source (uses same coordinate system as opengl) + * Returns the size of the sounds sample */ - void set_source_pos( ALfloat *pos ); + inline int get_size() const { return _size; } /** - * Set "constant" offset position of sound source (uses same - * coordinate system as opengl) + * Set position of the sound source (uses same coordinate system as opengl) */ - void set_offset_pos( ALfloat *pos ); + void set_base_position( SGVec3d pos ); + void set_relative_position( SGVec3f pos ); + + /** + * Get position of the sound source (uses same coordinate system as opengl) + */ + inline float *get_position() const { return toVec3f(_absolute_pos).data(); } /** * Set the orientation of the sound source, both for direction * and audio cut-off angles. */ - void set_orientation( ALfloat *dir, ALfloat inner_angle=360.0, - ALfloat outer_angle=360.0, - ALfloat outer_gain=0.0); + void set_orientation( SGVec3f dir ); + + /** + * Define the audio cone parameters for directional audio + */ + inline void set_audio_cone( float inner, float outer, float gain ) { + _inner_angle = inner; + _outer_angle = outer; + _outer_gain = gain; + _static_changed = true; + } + + /** + * Get the orientation of the sound source, the inner or outer angle + * or outer gain. + */ + inline float *get_orientation() { return _direction.data(); } + inline float *get_direction() { return _direction.data(); } + inline float get_innerangle() { return _inner_angle; } + inline float get_outerangle() { return _outer_angle; } + inline float get_outergain() { return _outer_gain; } + + /** + * Set velocity of the sound source (uses same coordinate system as opengl) + */ + inline void set_velocity( SGVec3f vel ) { + _velocity = SGVec3f(vel); _changed = true; + } /** - * Set velocity of sound source (uses same coordinate system as opengl) + * Get velocity of the sound source (uses same coordinate system as opengl) */ - void set_source_vel( ALfloat *vel, ALfloat *listener_vel ); + inline float *get_velocity() { return _velocity.data(); } /** * Set reference distance of sound (the distance where the gain * will be half.) */ - void set_reference_dist( ALfloat dist ); + inline void set_reference_dist( float dist ) { + _reference_dist = dist; _static_changed = true; + } + + /** + * Get reference distance of sound (the distance where the gain + * will be half.) + */ + inline float get_reference_dist() { return _reference_dist; } /** * Set maximum distance of sound (the distance where the sound is * no longer audible. */ - void set_max_dist( ALfloat dist ); + void set_max_dist( float dist ) { + _max_dist = dist; _static_changed = true; + } + + /** + * Get maximum istance of sound (the distance where the sound is + * no longer audible. + */ + inline float get_max_dist() { return _max_dist; } /** - * Load a sound file into a memory buffer only. + * Get the name of this sample */ - ALvoid* load_file(const char *path, const char *file); + inline string get_sample_name() { return _sample_name; } }; diff --git a/simgear/sound/soundmgr_openal.cxx b/simgear/sound/soundmgr_openal.cxx index fb49d247..543960e0 100644 --- a/simgear/sound/soundmgr_openal.cxx +++ b/simgear/sound/soundmgr_openal.cxx @@ -18,8 +18,8 @@ // General Public License for more details. // // You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// along with this program; if not, write to the Free Software Foundation, +// Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. // // $Id$ @@ -27,173 +27,192 @@ # include #endif -#include - -#if defined(__APPLE__) -# include -# include +#if defined( __APPLE__ ) +# include #else -# include -# include -#endif - -#if defined (__APPLE__) -# ifdef __GNUC__ -# if ( __GNUC__ >= 3 ) && ( __GNUC_MINOR__ >= 3 ) -// # include -inline int (isnan)(double r) { return !(r <= 0 || r >= 0); } -# else - // any C++ header file undefines isinf and isnan - // so this should be included before - // the functions are STILL in libm (libSystem on mac os x) -extern "C" int isnan (double); -extern "C" int isinf (double); -# endif -# else -// inline int (isinf)(double r) { return isinf(r); } -// inline int (isnan)(double r) { return isnan(r); } -# endif -#endif - -#if defined (__FreeBSD__) -# if __FreeBSD_version < 500000 - extern "C" { - inline int isnan(double r) { return !(r <= 0 || r >= 0); } - } -# endif +# include #endif -#if defined (__CYGWIN__) -#include -#endif - - #include +#include "soundmgr_openal.hxx" + +#include #include #include +#include -#include "soundmgr_openal.hxx" -#if defined(__MINGW32__) -#define isnan(x) _isnan(x) -#endif +#define MAX_SOURCES 128 // // Sound Manager // +int SGSoundMgr::_alut_init = 0; + // constructor -SGSoundMgr::SGSoundMgr() { +SGSoundMgr::SGSoundMgr() : + _working(false), + _changed(true), + _volume(0.5), + _device(NULL), + _context(NULL), + _listener_pos(SGVec3d::zeros().data()), + _listener_vel(SGVec3f::zeros().data()), + _devname(NULL) +{ +#if defined(ALUT_API_MAJOR_VERSION) && ALUT_API_MAJOR_VERSION >= 1 + if (_alut_init == 0) { + if ( !alutInitWithoutContext(NULL, NULL) ) { + testForALUTError("alut initialization"); + return; + } + _alut_init++; + } + _alut_init++; +#endif +} - SG_LOG( SG_GENERAL, SG_INFO, "Initializing OpenAL sound manager" ); +// destructor - // initialize OpenAL +SGSoundMgr::~SGSoundMgr() { + stop(); #if defined(ALUT_API_MAJOR_VERSION) && ALUT_API_MAJOR_VERSION >= 1 - if (!alutInit(NULL, NULL)) - { - ALenum error = alutGetError (); - SG_LOG( SG_GENERAL, SG_ALERT, "Audio initialization failed!" ); - SG_LOG( SG_GENERAL, SG_ALERT, " "+string(alutGetErrorString(error))); - working = false; - context = 0; - return; + _alut_init--; + if (_alut_init == 0) { + alutExit (); } - else - { - working = true; - context = alcGetCurrentContext(); +#endif +} + +// initialize the sound manager +void SGSoundMgr::init() { + + SG_LOG( SG_GENERAL, SG_INFO, "Initializing OpenAL sound manager" ); + + ALCdevice *device = alcOpenDevice(_devname); + if ( testForError(device, "No default audio device available.") ) { + return; } -#else - if ( (dev = alcOpenDevice( NULL )) != NULL - && ( context = alcCreateContext( dev, NULL )) != NULL ) { - working = true; - alcMakeContextCurrent( context ); - } else { - working = false; - context = 0; - SG_LOG( SG_GENERAL, SG_ALERT, "Audio initialization failed!" ); + + ALCcontext *context = alcCreateContext(device, NULL); + if ( testForError(context, "Unable to create a valid context.") ) { return; } -#endif - listener_pos[0] = 0.0; - listener_pos[1] = 0.0; - listener_pos[2] = 0.0; - - listener_vel[0] = 0.0; - listener_vel[1] = 0.0; - listener_vel[2] = 0.0; - - listener_ori[0] = 0.0; - listener_ori[1] = 0.0; - listener_ori[2] = -1.0; - listener_ori[3] = 0.0; - listener_ori[4] = 1.0; - listener_ori[5] = 0.0; - - alListenerf( AL_GAIN, 0.0f ); - alListenerfv( AL_POSITION, listener_pos ); - alListenerfv( AL_VELOCITY, listener_vel ); - alListenerfv( AL_ORIENTATION, listener_ori ); - alGetError(); - if ( alGetError() != AL_NO_ERROR) { - SG_LOG( SG_GENERAL, SG_ALERT, - "Oops AL error after audio initialization!" ); + if ( !alcMakeContextCurrent(context) ) { + testForALCError("context initialization"); + return; } - // exaggerate the ear candy? + _context = context; + _working = true; + + _listener_ori[0] = 0.0; _listener_ori[1] = 0.0; _listener_ori[2] = -1.0; + _listener_ori[3] = 0.0; _listener_ori[4] = 1.0; _listener_ori[5] = 0.0; + + alListenerf( AL_GAIN, 0.2f ); + alListenerfv( AL_POSITION, toVec3f(_listener_pos).data() ); + alListenerfv( AL_ORIENTATION, _listener_ori ); + alListenerfv( AL_VELOCITY, _listener_vel.data() ); + alDopplerFactor(1.0); - alDopplerVelocity(340.0); // speed of sound in meters per second. -} + alDopplerVelocity(340.3); // speed of sound in meters per second. -// destructor + if ( alIsExtensionPresent((const ALchar*)"EXT_exponent_distance") ) { + alDistanceModel(AL_EXPONENT_DISTANCE); + } else { + alDistanceModel(AL_INVERSE_DISTANCE); + } -SGSoundMgr::~SGSoundMgr() { + testForALError("listener initialization"); -#if defined(ALUT_API_MAJOR_VERSION) && ALUT_API_MAJOR_VERSION >= 1 - alutExit (); -#else - if (context) - alcDestroyContext( context ); -#endif + alGetError(); // clear any undetetced error, just to be sure + + // get a free source one at a time + // if an error is returned no more (hardware) sources are available + for (unsigned int i=0; isecond; + sgrp->suspend(); + } + } } void SGSoundMgr::bind () { - // no properties + _free_sources.clear(); + _free_sources.reserve( MAX_SOURCES ); + _sources_in_use.clear(); + _sources_in_use.reserve( MAX_SOURCES ); } void SGSoundMgr::unbind () { - // no properties + _sample_groups.clear(); + + // delete free sources + for (unsigned int i=0; i<_free_sources.size(); i++) { + ALuint source = _free_sources.at( i ); + alDeleteSources( 1 , &source ); + } + + _free_sources.clear(); + _sources_in_use.clear(); } // run the audio scheduler void SGSoundMgr::update( double dt ) { -} - + if (_working) { + sample_group_map_iterator sample_grp_current = _sample_groups.begin(); + sample_group_map_iterator sample_grp_end = _sample_groups.end(); + for ( ; sample_grp_current != sample_grp_end; ++sample_grp_current ) { + SGSampleGroup *sgrp = sample_grp_current->second; + sgrp->update(dt); + } -void -SGSoundMgr::pause () -{ - if (context) { - alcSuspendContext( context ); - if ( alGetError() != AL_NO_ERROR) { - SG_LOG( SG_GENERAL, SG_ALERT, - "Oops AL error after soundmgr pause()!" ); + if (_changed) { + alListenerf( AL_GAIN, _volume ); + alListenerfv( AL_VELOCITY, _listener_vel.data() ); + alListenerfv( AL_ORIENTATION, _listener_ori ); + alListenerfv( AL_POSITION, toVec3f(_listener_pos).data() ); + // alDopplerVelocity(340.3); // TODO: altitude dependent + testForALError("update"); + _changed = false; } } } @@ -202,151 +221,241 @@ SGSoundMgr::pause () void SGSoundMgr::resume () { - if (context) { - alcProcessContext( context ); - if ( alGetError() != AL_NO_ERROR) { - SG_LOG( SG_GENERAL, SG_ALERT, - "Oops AL error after soundmgr resume()!" ); + if (_working) { + sample_group_map_iterator sample_grp_current = _sample_groups.begin(); + sample_group_map_iterator sample_grp_end = _sample_groups.end(); + for ( ; sample_grp_current != sample_grp_end; ++sample_grp_current ) { + SGSampleGroup *sgrp = sample_grp_current->second; + sgrp->resume(); } } } -// add a sound effect, return true if successful -bool SGSoundMgr::add( SGSoundSample *sound, const string& refname ) { - - sample_map_iterator sample_it = samples.find( refname ); - if ( sample_it != samples.end() ) { - // sound already exists +// add a sampel group, return true if successful +bool SGSoundMgr::add( SGSampleGroup *sgrp, const string& refname ) +{ + sample_group_map_iterator sample_grp_it = _sample_groups.find( refname ); + if ( sample_grp_it != _sample_groups.end() ) { + // sample group already exists return false; } - samples[refname] = sound; + _sample_groups[refname] = sgrp; return true; } // remove a sound effect, return true if successful -bool SGSoundMgr::remove( const string &refname ) { - - sample_map_iterator sample_it = samples.find( refname ); - if ( sample_it != samples.end() ) { - // first stop the sound from playing (so we don't bomb the - // audio scheduler) - samples.erase( sample_it ); - - // cout << "sndmgr: removed -> " << refname << endl; - return true; - } else { - // cout << "sndmgr: failed remove -> " << refname << endl; +bool SGSoundMgr::remove( const string &refname ) +{ + sample_group_map_iterator sample_grp_it = _sample_groups.find( refname ); + if ( sample_grp_it == _sample_groups.end() ) { + // sample group was not found. return false; } + + _sample_groups.erase( refname ); + + return true; } // return true of the specified sound exists in the sound manager system bool SGSoundMgr::exists( const string &refname ) { - sample_map_iterator sample_it = samples.find( refname ); - if ( sample_it != samples.end() ) { - return true; - } else { + sample_group_map_iterator sample_grp_it = _sample_groups.find( refname ); + if ( sample_grp_it == _sample_groups.end() ) { + // sample group was not found. return false; } + + return true; } -// return a pointer to the SGSoundSample if the specified sound exists +// return a pointer to the SGSampleGroup if the specified sound exists // in the sound manager system, otherwise return NULL -SGSoundSample *SGSoundMgr::find( const string &refname ) { - sample_map_iterator sample_it = samples.find( refname ); - if ( sample_it != samples.end() ) { - return sample_it->second; - } else { - return NULL; +SGSampleGroup *SGSoundMgr::find( const string &refname, bool create ) { + sample_group_map_iterator sample_grp_it = _sample_groups.find( refname ); + if ( sample_grp_it == _sample_groups.end() ) { + // sample group was not found. + if (create) { + SGSampleGroup* sgrp = new SGSampleGroup(this, refname); + return sgrp; + } + else + return NULL; } -} + return sample_grp_it->second; +} -// tell the scheduler to play the indexed sample in a continuous -// loop -bool SGSoundMgr::play_looped( const string &refname ) { - SGSoundSample *sample; - if ( (sample = find( refname )) == NULL ) { - return false; - } else { - sample->play( true ); - return true; - } +void SGSoundMgr::set_volume( float v ) +{ + _volume = v; + if (_volume > 1.0) _volume = 1.0; + if (_volume < 0.0) _volume = 0.0; + _changed = true; } +// Get an unused source id +// +// The Sound Manager should keep track of the sources in use, the distance +// of these sources to the listener and the volume (also based on audio cone +// and hence orientation) of the sources. +// +// The Sound Manager is (and should be) the only one knowing about source +// management. Sources further away should be suspendped to free resources for +// newly added sounds close by. +unsigned int SGSoundMgr::request_source() +{ + unsigned int source = NO_SOURCE; -// tell the scheduler to play the indexed sample once -bool SGSoundMgr::play_once( const string& refname ) { - SGSoundSample *sample; + if (_free_sources.size() > 0) { + source = _free_sources.back(); + _free_sources.pop_back(); - if ( (sample = find( refname )) == NULL ) { - return false; - } else { - sample->play( false ); - return true; + _sources_in_use.push_back(source); } -} - -// return true of the specified sound is currently being played -bool SGSoundMgr::is_playing( const string& refname ) { - SGSoundSample *sample; + return source; +} - if ( (sample = find( refname )) == NULL ) { - return false; - } else { - return ( sample->is_playing() ); +// 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; + + alGetSourcei( source, AL_SOURCE_STATE, &result ); + if ( result == AL_PLAYING ) { + alSourceStop( source ); + } + testForALError("free_source"); + + _free_sources.push_back(source); + _sources_in_use.erase(_sources_in_use.begin()+i, + _sources_in_use.begin()+i+1); + break; + } } } +bool SGSoundMgr::load(string &samplepath, void **dbuf, int *fmt, + unsigned int *sz, int *frq ) +{ + ALenum format = (ALenum)*fmt; + ALsizei size = (ALsizei)*sz; + ALsizei freq = (ALsizei)*frq; + ALvoid *data; -// immediate stop playing the sound -bool SGSoundMgr::stop( const string& refname ) { - SGSoundSample *sample; +#if defined(ALUT_API_MAJOR_VERSION) && ALUT_API_MAJOR_VERSION >= 1 + ALfloat freqf; + data = alutLoadMemoryFromFile(samplepath.c_str(), &format, &size, &freqf ); + freq = (ALsizei)freqf; + if (data == NULL) { + int error = alutGetError(); + string msg = "Failed to load wav file: "; + msg.append(alutGetErrorString(error)); + throw sg_io_exception(msg.c_str(), sg_location(samplepath)); + return false; + } - if ( (sample = find( refname )) == NULL ) { +#else + ALbyte *fname = (ALbyte *)samplepath.c_str(); +# if defined (__APPLE__) + alutLoadWAVFile( fname, &format, &data, &size, &freq ); +# else + ALboolean loop; + alutLoadWAVFile( fname, &format, &data, &size, &freq, &loop ); +# endif + ALenum error = alutGetError(); + if ( error != ALUT_ERROR_NO_ERROR ) { + string msg = "Failed to load wav file: "; + msg.append(alutGetErrorString(error)); + throw sg_io_exception(msg.c_str(), sg_location(samplepath)); return false; - } else { - sample->stop(); - return true; } +#endif + + *dbuf = (void *)data; + *fmt = (int)format; + *sz = (unsigned int)size; + *frq = (int)freq; + + return true; } -// set source position of all managed sounds -void SGSoundMgr::set_source_pos_all( ALfloat *pos ) { - if ( isnan(pos[0]) || isnan(pos[1]) || isnan(pos[2]) ) { - // bail if a bad position is passed in - return; +/** + * set the orientation of the listener (in opengl coordinates) + * + * Description: ORIENTATION is a pair of 3-tuples representing the + * 'at' direction vector and 'up' direction of the Object in + * Cartesian space. AL expects two vectors that are orthogonal to + * each other. These vectors are not expected to be normalized. If + * one or more vectors have zero length, implementation behavior + * is undefined. If the two vectors are linearly dependent, + * behavior is undefined. + */ +void SGSoundMgr::set_orientation( SGQuatd ori ) +{ + SGVec3d sgv_up = ori.rotate(SGVec3d::e2()); + SGVec3d sgv_at = ori.rotate(SGVec3d::e3()); + for (int i=0; i<3; i++) { + _listener_ori[i] = sgv_at[i]; + _listener_ori[i+3] = sgv_up[i]; } + _changed = true; +} - sample_map_iterator sample_current = samples.begin(); - sample_map_iterator sample_end = samples.end(); - for ( ; sample_current != sample_end; ++sample_current ) { - SGSoundSample *sample = sample_current->second; - sample->set_source_pos( pos ); - } + +bool SGSoundMgr::testForError(void *p, string s) +{ + if (p == NULL) { + SG_LOG( SG_GENERAL, SG_ALERT, "Error: " << s); + return true; + } + return false; } -// set source velocity of all managed sounds -void SGSoundMgr::set_source_vel_all( ALfloat *vel ) { - if ( isnan(vel[0]) || isnan(vel[1]) || isnan(vel[2]) ) { - // bail if a bad velocity is passed in - return; +bool SGSoundMgr::testForALError(string s) +{ + ALenum error = alGetError(); + if (error != AL_NO_ERROR) { + SG_LOG( SG_GENERAL, SG_ALERT, "AL Error (sound manager): " + << alGetString(error) << " at " << s); + return true; } + return false; +} + +bool SGSoundMgr::testForALCError(string s) +{ + ALCenum error; + error = alcGetError(_device); + if (error != ALC_NO_ERROR) { + SG_LOG( SG_GENERAL, SG_ALERT, "ALC Error (sound manager): " + << alcGetString(_device, error) << " at " + << s); + return true; + } + return false; +} - sample_map_iterator sample_current = samples.begin(); - sample_map_iterator sample_end = samples.end(); - for ( ; sample_current != sample_end; ++sample_current ) { - SGSoundSample *sample = sample_current->second; - sample->set_source_vel( vel, listener_vel ); +bool SGSoundMgr::testForALUTError(string s) +{ + ALenum error; + error = alutGetError (); + if (error != ALUT_ERROR_NO_ERROR) { + SG_LOG( SG_GENERAL, SG_ALERT, "ALUT Error (sound manager): " + << alutGetErrorString(error) << " at " + << s); + return true; } + return false; } diff --git a/simgear/sound/soundmgr_openal.hxx b/simgear/sound/soundmgr_openal.hxx index 59cb24e4..96109057 100644 --- a/simgear/sound/soundmgr_openal.hxx +++ b/simgear/sound/soundmgr_openal.hxx @@ -37,113 +37,68 @@ # error This library requires C++ #endif -#include - #include +#include #include -#if defined( __APPLE__ ) +#if defined(__APPLE__) +# define AL_ILLEGAL_ENUM AL_INVALID_ENUM +# define AL_ILLEGAL_COMMAND AL_INVALID_OPERATION # include -# include +# include #else # include -# include +# include #endif +#include +#include +#include + +#include "sample_group.hxx" #include "sample_openal.hxx" using std::map; using std::string; - -typedef map < string, SGSharedPtr > sample_map; -typedef sample_map::iterator sample_map_iterator; -typedef sample_map::const_iterator const_sample_map_iterator; +typedef 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 SGSoundSample instances + * Manage a collection of SGSampleGroup instances */ -class SGSoundMgr +class SGSoundMgr : public SGSubsystem { - - ALCdevice *dev; - ALCcontext *context; - - // Position of the listener. - ALfloat listener_pos[3]; - - // Velocity of the listener. - ALfloat listener_vel[3]; - - // Orientation of the listener. (first 3 elements are "at", second - // 3 are "up") - ALfloat listener_ori[6]; - - sample_map samples; - - bool working; - double safety; - public: SGSoundMgr(); ~SGSoundMgr(); - - /** - * (re) initialize the sound manager. - */ void init(); - - - /** - * Bind properties for the sound manager. - */ void bind(); - - - /** - * Unbind properties for the sound manager. - */ void unbind(); - - - /** - * Run the audio scheduler. - */ void update(double dt); - - - /** - * Pause all sounds. - */ - void pause(); - - - /** - * Resume all sounds. - */ + + void suspend(); void resume(); + void stop(); + inline void reinit() { stop(); init(); } /** * is audio working? */ - inline bool is_working() const { return working; } + inline bool is_working() const { return _working; } /** - * reinitialize the sound manager + * add a sample group, return true if successful */ - inline void reinit() { init(); } - - /** - * add a sound effect, return true if successful - */ - bool add( SGSoundSample *sound, const string& refname); + bool add( SGSampleGroup *sgrp, const string& refname ); /** - * remove a sound effect, return true if successful + * remove a sample group, return true if successful */ bool remove( const string& refname ); @@ -153,94 +108,91 @@ public: bool exists( const string& refname ); /** - * return a pointer to the SGSoundSample if the specified sound + * return a pointer to the SGSampleGroup if the specified sound * exists in the sound manager system, otherwise return NULL */ - SGSoundSample *find( const string& refname ); - - /** - * tell the scheduler to play the indexed sample in a continuous - * loop - */ - bool play_looped( const string& refname ); + SGSampleGroup *find( const string& refname, bool create = false ); /** - * tell the scheduler to play the indexed sample once - */ - bool play_once( const string& refname ); - - /** - * return true of the specified sound is currently being played - */ - bool is_playing( const string& refname ); - - /** - * immediate stop playing the sound - */ - bool stop( const string& refname ); - - /** - * set overall volume for the application. - * @param vol 1.0 is default, must be greater than 0 + * set the position of the listener (in opengl coordinates) */ - inline void set_volume( const ALfloat vol ) { - if ( vol > 0.0 ) { - alListenerf( AL_GAIN, vol ); - } + inline void set_position( SGVec3d pos ) { + _listener_pos = pos; + _changed = true; } - /** - * set the position of the listener (in opengl coordinates) - */ - inline void set_listener_pos( ALfloat *pos ) { - listener_pos[0] = pos[0]; - listener_pos[1] = pos[1]; - listener_pos[2] = pos[2]; - alListenerfv( AL_POSITION, listener_pos ); + inline double *get_position() { + return _listener_pos.data(); } /** * set the velocity of the listener (in opengl coordinates) */ - inline void set_listener_vel( ALfloat *vel ) { - listener_vel[0] = vel[0]; - listener_vel[1] = vel[1]; - listener_vel[2] = vel[2]; -#ifdef USE_OPEN_AL_DOPPLER - alListenerfv( AL_VELOCITY, listener_vel ); -#endif + inline void set_velocity( SGVec3f vel ) { + _listener_vel = vel; + _changed = true; } /** * set the orientation of the listener (in opengl coordinates) - * - * Description: ORIENTATION is a pair of 3-tuples representing the - * 'at' direction vector and 'up' direction of the Object in - * Cartesian space. AL expects two vectors that are orthogonal to - * each other. These vectors are not expected to be normalized. If - * one or more vectors have zero length, implementation behavior - * is undefined. If the two vectors are linearly dependent, - * behavior is undefined. */ - inline void set_listener_orientation( ALfloat *ori ) { - listener_ori[0] = ori[0]; - listener_ori[1] = ori[1]; - listener_ori[2] = ori[2]; - listener_ori[3] = ori[3]; - listener_ori[4] = ori[4]; - listener_ori[5] = ori[5]; - alListenerfv( AL_ORIENTATION, listener_ori ); - } + void set_orientation( SGQuatd ori ); + + enum { + NO_SOURCE = (unsigned int)-1, + NO_BUFFER = (unsigned int)-1 + }; + + void set_volume( float v ); + inline float get_volume() { return _volume; } /** - * set the positions of all managed sound sources + * get a new OpenAL source id + * returns NO_SOURCE is no source is available */ - void set_source_pos_all( ALfloat *pos ); + unsigned int request_source(); /** - * set the velocities of all managed sound sources + * give back an OpenAL source id for further use. */ - void set_source_vel_all( ALfloat *pos ); + void release_source( unsigned int source ); + + bool load(string &samplepath, void **data, int *format, unsigned int*size, + int *freq ); + + +private: + static int _alut_init; + + bool _working; + bool _changed; + float _volume; + + ALCdevice *_device; + ALCcontext *_context; + + // Position of the listener. + SGVec3d _listener_pos; + + // Velocity of the listener. + SGVec3f _listener_vel; + + // Orientation of the listener. + // first 3 elements are "at" vector, second 3 are "up" vector + ALfloat _listener_ori[6]; + + sample_group_map _sample_groups; + + vector _free_sources; + vector _sources_in_use; + + char *_devname; + + bool testForALError(string s); + bool testForALCError(string s); + bool testForALUTError(string s); + bool testForError(void *p, string s); + void update_sample_config( SGSampleGroup *sound ); }; diff --git a/simgear/sound/xmlsound.cxx b/simgear/sound/xmlsound.cxx index 8dd8df93..919f0837 100644 --- a/simgear/sound/xmlsound.cxx +++ b/simgear/sound/xmlsound.cxx @@ -50,14 +50,11 @@ static const struct { const char *name; double (*fn)(double); } __sound_fn[] = { -// {"lin", _snd_lin}, {"inv", _snd_inv}, {"abs", _snd_abs}, {"sqrt", _snd_sqrt}, {"log", _snd_log10}, {"ln", _snd_log}, -// {"sqr", _snd_sqr}, -// {"pow3", _snd_pow3}, {"", NULL} }; @@ -84,18 +81,14 @@ SGXmlSound::~SGXmlSound() } void -SGXmlSound::init(SGPropertyNode *root, SGPropertyNode *node, SGSoundMgr *sndmgr, - const string &path) +SGXmlSound::init(SGPropertyNode *root, SGPropertyNode *node, + SGSampleGroup *sgrp, const string &path) { // // set global sound properties // - if (sndmgr->is_working() == false) { - return; - } - _name = node->getStringValue("name", ""); SG_LOG(SG_GENERAL, SG_DEBUG, "Loading sound information for: " << _name ); @@ -236,8 +229,7 @@ SGXmlSound::init(SGPropertyNode *root, SGPropertyNode *node, SGSoundMgr *sndmgr, // // Relative position // - sgVec3 offset_pos; - sgSetVec3( offset_pos, 0.0, 0.0, 0.0 ); + SGVec3f offset_pos = SGVec3f::zeros(); SGPropertyNode_ptr pos = node->getChild("position"); if ( pos != NULL ) { offset_pos[0] = pos->getDoubleValue("x", 0.0); @@ -248,9 +240,8 @@ SGXmlSound::init(SGPropertyNode *root, SGPropertyNode *node, SGSoundMgr *sndmgr, // // Orientation // - sgVec3 dir; + SGVec3f dir = SGVec3f::zeros(); float inner, outer, outer_gain; - sgSetVec3( dir, 0.0, 0.0, 0.0 ); inner = outer = 360.0; outer_gain = 0.0; pos = node->getChild("orientation"); @@ -266,8 +257,8 @@ SGXmlSound::init(SGPropertyNode *root, SGPropertyNode *node, SGSoundMgr *sndmgr, // // Initialize the sample // - _mgr = sndmgr; - if ( (_sample = _mgr->find(_name)) == NULL ) { + _sgrp = sgrp; + if ( (_sample = _sgrp->find(_name)) == NULL ) { // FIXME: Does it make sense to overwrite a previous entry's // configuration just because a new entry has the same name? // Note that we can't match on identical "path" because we the @@ -276,17 +267,16 @@ SGXmlSound::init(SGPropertyNode *root, SGPropertyNode *node, SGSoundMgr *sndmgr, // "alSource". The semantics of what is going on here seems // confused and needs to be thought through more carefully. _sample = new SGSoundSample( path.c_str(), - node->getStringValue("path", ""), - false ); - - _mgr->add( _sample, _name ); + node->getStringValue("path", "") ); + _sgrp->add( _sample, _name ); } - _sample->set_offset_pos( offset_pos ); - _sample->set_orientation(dir, inner, outer, outer_gain); - _sample->set_volume(v); + _sample->set_relative_position( offset_pos ); + _sample->set_orientation( dir ); + _sample->set_audio_cone(inner, outer, outer_gain); _sample->set_reference_dist( reference_dist ); _sample->set_max_dist( max_dist ); + _sample->set_volume(v); _sample->set_pitch(p); } @@ -316,7 +306,8 @@ SGXmlSound::update (double dt) ) ) { - if ((_mode != SGXmlSound::IN_TRANSIT) || (_stopping > MAX_TRANSIT_TIME)) { + if ((_mode != SGXmlSound::IN_TRANSIT) || (_stopping > MAX_TRANSIT_TIME)) + { if (_sample->is_playing()) { SG_LOG(SG_GENERAL, SG_DEBUG, "Stopping audio after " << _dt_play << " sec: " << _name ); diff --git a/simgear/sound/xmlsound.hxx b/simgear/sound/xmlsound.hxx index d161bbca..b5bc1f9f 100644 --- a/simgear/sound/xmlsound.hxx +++ b/simgear/sound/xmlsound.hxx @@ -38,8 +38,8 @@ #include #include +#include "sample_group.hxx" #include "sample_openal.hxx" -#include "soundmgr_openal.hxx" static const double MAX_TRANSIT_TIME = 0.1; // 100 ms. @@ -99,10 +99,10 @@ public: * @param root The root node of the programs property tree. * @param child A pointer to the location of the current event as defined * in the configuration file. - * @param sndmgr A pointer to a pre-initialized sound manager class. + * @param sgrp A pointer to a pre-initialized sample group class. * @param path The path where the audio files remain. */ - virtual void init (SGPropertyNode *, SGPropertyNode *, SGSoundMgr *, + virtual void init (SGPropertyNode *, SGPropertyNode *, SGSampleGroup *, const string &); /** @@ -135,7 +135,7 @@ protected: private: - SGSoundMgr * _mgr; + SGSampleGroup * _sgrp; SGSharedPtr _sample; SGSharedPtr _condition; diff --git a/simgear/structure/SGAtomic.cxx b/simgear/structure/SGAtomic.cxx index ee219d84..a31478f5 100644 --- a/simgear/structure/SGAtomic.cxx +++ b/simgear/structure/SGAtomic.cxx @@ -20,7 +20,7 @@ #include "SGAtomic.hxx" -#if defined(SGATOMIC_USE_GCC4_BUILTINS) && defined(__i386__) +#if !defined(SGATOMIC_USE_GCC4_BUILTINS) && defined (__i386__) // Usually the apropriate functions are inlined by gcc. // But if gcc is called with something aequivalent to -march=i386, -- 2.39.5