From: ehofman Date: Sun, 4 Oct 2009 13:52:27 +0000 (+0000) Subject: Initial commit of the new sound system, expect more updates to follow X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=e2e1524454d94db9e1b8cf591f337ca0a5fd06da;p=simgear.git Initial commit of the new sound system, expect more updates to follow --- 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 891c84d0..a06f2afe 100644 Binary files a/simgear/sound/jet.wav and b/simgear/sound/jet.wav differ 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,