#include <simgear/math/sg_geodesy.hxx>
#include <simgear/math/point3d.hxx>
#include <simgear/math/polar3d.hxx>
-#include <simgear/sound/soundmgr_openal.hxx>
+#include <simgear/sound/sample_group.hxx>
#include <simgear/scene/sky/cloudfield.hxx>
#include <simgear/scene/sky/newcloud.hxx>
#include <simgear/props/props.hxx>
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),
}
SGEnviro::~SGEnviro(void) {
+ if (sampleGroup) delete sampleGroup;
+
// OSGFIXME
return;
list_of_lightning::iterator iLightning;
}
-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) {
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 );
double ax = 0.0, ay = 0.0;
ax = cos(course) * dist;
ay = sin(course) * dist;
- SGSharedPtr<SGSoundSample> snd = soundMgr->find("thunder");
+ SGSharedPtr<SGSoundSample> 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;
}
using std::string;
class SGLightning;
-class SGSoundMgr;
+class SGSampleGroup;
/**
* Simulate some echo on a weather radar.
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;
* 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 );
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
#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\"
#include <unistd.h> // sleep()
#endif
-#include "sample_openal.hxx"
+#include <simgear/debug/logstream.hxx>
+#include <simgear/misc/sg_path.hxx>
+
#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;
}
--- /dev/null
+#ifdef HAVE_CONFIG_H
+# include <simgear_config.h>
+#endif
+
+#include <simgear/compiler.h>
+
+#if defined (__APPLE__)
+# ifdef __GNUC__
+# if ( __GNUC__ >= 3 ) && ( __GNUC_MINOR__ >= 3 )
+// # include <math.h>
+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 <iostream>
+ // 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 <ieeefp.h>
+#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;
+}
+
--- /dev/null
+// soundmgr.hxx -- Sound effect management class
+//
+// Sampel Group handler initially written by Erik Hofman
+//
+// Copyright (C) 2009 Erik Hofman - <erik@ehofman.com>
+//
+// 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 <OpenAL/al.h>
+#else
+# include <AL/al.h>
+#endif
+
+#include <string>
+#include <map>
+
+#include <simgear/compiler.h>
+#include <simgear/math/SGMath.hxx>
+#include <simgear/structure/SGReferenced.hxx>
+#include <simgear/structure/SGSharedPtr.hxx>
+#include <simgear/structure/exception.hxx>
+
+#include "sample_openal.hxx"
+
+using std::map;
+using std::string;
+
+typedef map < string, SGSharedPtr<SGSoundSample> > 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
+
// 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$
# include <simgear_config.h>
#endif
-#if defined( __APPLE__ )
-# define AL_ILLEGAL_ENUM AL_INVALID_ENUM
-# define AL_ILLEGAL_COMMAND AL_INVALID_OPERATION
-# include <OpenAL/al.h>
-# include <OpenAL/alut.h>
-#else
-# include <AL/al.h>
-# include <AL/alut.h>
-#endif
-
#include <simgear/debug/logstream.hxx>
-#include <simgear/misc/sg_path.hxx>
#include <simgear/structure/exception.hxx>
+#include <simgear/misc/sg_path.hxx>
+#include <simgear/math/SGQuat.hxx>
+#include "soundmgr_openal.hxx"
#include "sample_openal.hxx"
// 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);
}
-
# error This library requires C++
#endif
-#include <simgear/compiler.h>
-
#include <string>
+#include <simgear/compiler.h>
#include <simgear/debug/logstream.hxx>
#include <simgear/structure/SGReferenced.hxx>
#include <simgear/structure/SGSharedPtr.hxx>
+#include <simgear/math/SGMath.hxx>
#include <plib/sg.h>
-#if defined(__APPLE__)
-# define AL_ILLEGAL_ENUM AL_INVALID_ENUM
-# define AL_ILLEGAL_COMMAND AL_INVALID_OPERATION
-# include <OpenAL/al.h>
-# include <OpenAL/alut.h>
-#else
-# include <AL/al.h>
-# include <AL/alut.h>
-#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;
/**
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:
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.
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.
* 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; }
};
// 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$
# include <simgear_config.h>
#endif
-#include <simgear/compiler.h>
-
-#if defined(__APPLE__)
-# include <OpenAL/al.h>
-# include <OpenAL/alc.h>
+#if defined( __APPLE__ )
+# include <OpenAL/alut.h>
#else
-# include <AL/al.h>
-# include <AL/alc.h>
-#endif
-
-#if defined (__APPLE__)
-# ifdef __GNUC__
-# if ( __GNUC__ >= 3 ) && ( __GNUC_MINOR__ >= 3 )
-// # include <math.h>
-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 <iostream>
- // 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 <AL/alut.h>
#endif
-#if defined (__CYGWIN__)
-#include <ieeefp.h>
-#endif
-
-
#include <iostream>
+#include "soundmgr_openal.hxx"
+
+#include <simgear/structure/exception.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/misc/sg_path.hxx>
+#include <simgear/math/SGMath.hxx>
-#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; i<MAX_SOURCES; i++) {
+ ALuint source;
+ ALenum error;
+
+ alGetError();
+ alGenSources(1, &source);
+ error = alGetError();
+ if ( error == AL_NO_ERROR ) {
+ _free_sources.push_back( source );
+ }
+ else break;
+ }
}
+// suspend the sound manager
+void SGSoundMgr::stop() {
+ if (_working) {
+ _working = false;
-// initialize the sound manager
-void SGSoundMgr::init() {
- //
- // Remove the samples from the sample manager.
- //
- samples.clear();
+ _context = alcGetCurrentContext();
+ _device = alcGetContextsDevice(_context);
+ alcMakeContextCurrent(NULL);
+ alcDestroyContext(_context);
+ alcCloseDevice(_device);
+ }
+}
+
+void SGSoundMgr::suspend() {
+ 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->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;
}
}
}
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;
}
# error This library requires C++
#endif
-#include <simgear/compiler.h>
-
#include <string>
+#include <vector>
#include <map>
-#if defined( __APPLE__ )
+#if defined(__APPLE__)
+# define AL_ILLEGAL_ENUM AL_INVALID_ENUM
+# define AL_ILLEGAL_COMMAND AL_INVALID_OPERATION
# include <OpenAL/al.h>
-# include <OpenAL/alc.h>
+# include <OpenAL/alut.h>
#else
# include <AL/al.h>
-# include <AL/alc.h>
+# include <AL/alut.h>
#endif
+#include <simgear/compiler.h>
+#include <simgear/structure/subsystem_mgr.hxx>
+#include <simgear/math/SGMathFwd.hxx>
+
+#include "sample_group.hxx"
#include "sample_openal.hxx"
using std::map;
using std::string;
-
-typedef map < string, SGSharedPtr<SGSoundSample> > sample_map;
-typedef sample_map::iterator sample_map_iterator;
-typedef sample_map::const_iterator const_sample_map_iterator;
+typedef map < string, SGSharedPtr<SGSampleGroup> > sample_group_map;
+typedef sample_group_map::iterator sample_group_map_iterator;
+typedef sample_group_map::const_iterator const_sample_group_map_iterator;
/**
- * Manage a collection of 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 );
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<ALuint> _free_sources;
+ vector<ALuint> _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 );
};
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}
};
}
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 );
//
// 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);
//
// 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");
//
// 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
// "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);
}
)
)
{
- 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 );
#include <simgear/compiler.h>
#include <simgear/props/condition.hxx>
+#include "sample_group.hxx"
#include "sample_openal.hxx"
-#include "soundmgr_openal.hxx"
static const double MAX_TRANSIT_TIME = 0.1; // 100 ms.
* @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 &);
/**
private:
- SGSoundMgr * _mgr;
+ SGSampleGroup * _sgrp;
SGSharedPtr<SGSoundSample> _sample;
SGSharedPtr<SGCondition> _condition;
#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,