From: curt Date: Sat, 24 Apr 2004 19:02:29 +0000 (+0000) Subject: Rewrite the entire audio support library on top of OpenAL rather than plib's X-Git-Url: https://git.mxchange.org/?a=commitdiff_plain;h=5bbcd386fa837885782f128af77ff0162761945c;p=simgear.git Rewrite the entire audio support library on top of OpenAL rather than plib's sound manager. The interface was simplified and cleaned up a bit, and I haven't back ported these changes to the plib sound wrappers ... we could I suppose if someone really had a problem, but I haven't seen anything so far that would indicate the extra effort is worth it. --- diff --git a/configure.ac b/configure.ac index 9d448213..fb53c8a3 100644 --- a/configure.ac +++ b/configure.ac @@ -270,7 +270,21 @@ esac opengl_LIBS="$LIBS" LIBS="$base_LIBS" +dnl check for OpenAL libraries +case "${host}" in +*) + dnl default unix style machines + + AC_SEARCH_LIBS(alGenBuffers, openal) + ;; + +esac + +openal_LIBS="$LIBS" +LIBS="$base_LIBS" + AC_SUBST(base_LIBS) +AC_SUBST(openal_LIBS) AC_SUBST(opengl_LIBS) AC_SUBST(thread_LIBS) AC_SUBST(network_LIBS) diff --git a/simgear/sound/Makefile.am b/simgear/sound/Makefile.am index 4aa9f322..da59b224 100644 --- a/simgear/sound/Makefile.am +++ b/simgear/sound/Makefile.am @@ -1,11 +1,29 @@ includedir = @includedir@/sound +EXTRA_DIST = jet.wav + lib_LIBRARIES = libsgsound.a noinst_HEADERS = -include_HEADERS = sound.hxx soundmgr.hxx +include_HEADERS = \ + sample_openal.hxx \ + soundmgr_openal.hxx \ + xmlsound.hxx + +libsgsound_a_SOURCES = \ + sample_openal.cxx \ + soundmgr_openal.cxx \ + xmlsound.cxx + +noinst_PROGRAMS = openal_test1 openal_test2 + +openal_test1_SOURCES = openal_test1.cxx +openal_test2_SOURCES = openal_test2.cxx -libsgsound_a_SOURCES = sound.cxx soundmgr.cxx +openal_test1_LDADD = -lsgdebug -lopenal +openal_test2_LDADD = \ + $(top_builddir)/simgear/sound/libsgsound.a \ + -lsgdebug -lsgmisc -lsgstructure $(openal_LIBS) INCLUDES = -I$(top_srcdir) diff --git a/simgear/sound/jet.wav b/simgear/sound/jet.wav new file mode 100644 index 00000000..891c84d0 Binary files /dev/null and b/simgear/sound/jet.wav differ diff --git a/simgear/sound/openal_test1.cxx b/simgear/sound/openal_test1.cxx new file mode 100644 index 00000000..91b8c849 --- /dev/null +++ b/simgear/sound/openal_test1.cxx @@ -0,0 +1,125 @@ +#include + +#include +#include + +#include + +static void print_openal_error( ALuint error ) { + if ( error == AL_INVALID_NAME ) { + SG_LOG( SG_GENERAL, SG_ALERT, "AL_INVALID_NAME" ); + } else if ( error == AL_ILLEGAL_ENUM ) { + SG_LOG( SG_GENERAL, SG_ALERT, "AL_ILLEGAL_ENUM" ); + } else if ( error == AL_INVALID_VALUE ) { + SG_LOG( SG_GENERAL, SG_ALERT, "AL_INVALID_VALUE" ); + } else if ( error == AL_ILLEGAL_COMMAND ) { + SG_LOG( SG_GENERAL, SG_ALERT, "AL_ILLEGAL_COMMAND" ); + } else if ( error == AL_OUT_OF_MEMORY ) { + SG_LOG( SG_GENERAL, SG_ALERT, "AL_OUT_OF_MEMORY" ); + } else { + SG_LOG( SG_GENERAL, SG_ALERT, "Unhandled error code = " << error ); + } +} + + +int main( int argc, char *argv[] ) { + // initialize OpenAL + alutInit( 0, NULL ); + alGetError(); + if ( alGetError() != AL_NO_ERROR) { + SG_LOG( SG_GENERAL, SG_ALERT, "Audio initialization failed!" ); + } + + // 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]; + + 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; + + alListenerfv( AL_POSITION, listener_pos ); + alListenerfv( AL_VELOCITY, listener_vel ); + alListenerfv( AL_ORIENTATION, listener_ori ); + + // Buffers hold sound data. + ALuint buffer; + + // Sources are points emitting sound. + ALuint source; + + // Position of the source sound. + ALfloat source_pos[3]; + + // Velocity of the source sound. + ALfloat source_vel[3]; + + // configuration values + ALenum format; + ALsizei size; + ALvoid* data; + ALsizei freq; + ALboolean loop; + + source_pos[0] = 0.0; source_pos[1] = 0.0; source_pos[2] = 0.0; + source_vel[0] = 0.0; source_vel[1] = 0.0; source_vel[2] = 0.0; + + // create an OpenAL buffer handle + alGenBuffers(1, &buffer); + ALuint error = alGetError(); + if ( error != AL_NO_ERROR ) { + print_openal_error( error ); + SG_LOG( SG_GENERAL, SG_ALERT, "Failed to gen OpenAL buffer." ); + } else { + SG_LOG( SG_GENERAL, SG_ALERT, "Buffer created ok!" ); + } + + // Load the sample file + alutLoadWAVFile( (ALbyte *)"jet.wav", &format, &data, &size, &freq, &loop ); + if (alGetError() != AL_NO_ERROR) { + SG_LOG( SG_GENERAL, SG_ALERT, "Failed to load wav file."); + } + + // Copy data to the internal OpenAL buffer + alBufferData( buffer, format, data, size, freq ); + if (alGetError() != AL_NO_ERROR) { + SG_LOG( SG_GENERAL, SG_ALERT, "Failed to buffer data."); + } + + alutUnloadWAV( format, data, size, freq ); + + alGenSources(1, &source); + if (alGetError() != AL_NO_ERROR) { + print_openal_error( error ); + } + + alSourcei( source, AL_BUFFER, buffer ); + alSourcef( source, AL_PITCH, 1.0 ); + alSourcef( source, AL_GAIN, 1.0 ); + alSourcefv( source, AL_POSITION, source_pos ); + alSourcefv( source, AL_VELOCITY, source_vel ); + alSourcei( source, AL_LOOPING, loop ); + + alSourcePlay( source ); + + sleep(10); + + return 0; +} diff --git a/simgear/sound/openal_test2.cxx b/simgear/sound/openal_test2.cxx new file mode 100644 index 00000000..10884c59 --- /dev/null +++ b/simgear/sound/openal_test2.cxx @@ -0,0 +1,47 @@ +#include + +#include "sample_openal.hxx" +#include "soundmgr_openal.hxx" + + +int main( int argc, char *argv[] ) { + SGSoundMgr sm; + + SGSoundSample sample1( ".", "jet.wav" ); + sample1.set_volume(0.5); + sample1.set_volume(0.2); + sample1.play_looped(); + sleep(1); + + SGSoundSample sample2( ".", "jet.wav" ); + sample2.set_volume(0.5); + sample2.set_pitch(0.4); + sample2.play_looped(); + sleep(1); + + SGSoundSample sample3( ".", "jet.wav" ); + sample3.set_volume(0.5); + sample3.set_pitch(0.8); + sample3.play_looped(); + sleep(1); + + SGSoundSample sample4( ".", "jet.wav" ); + sample4.set_volume(0.5); + sample4.set_pitch(1.2); + sample4.play_looped(); + sleep(1); + + SGSoundSample sample5( ".", "jet.wav" ); + sample5.set_volume(0.5); + sample5.set_pitch(1.6); + sample5.play_looped(); + sleep(1); + + SGSoundSample sample6( ".", "jet.wav" ); + sample6.set_volume(0.5); + sample6.set_pitch(2.0); + sample6.play_looped(); + sleep(1); + + sleep(10); +} diff --git a/simgear/sound/sample_openal.cxx b/simgear/sound/sample_openal.cxx new file mode 100644 index 00000000..a4459788 --- /dev/null +++ b/simgear/sound/sample_openal.cxx @@ -0,0 +1,181 @@ +// sample.cxx -- Sound sample encapsulation class +// +// Written by Curtis Olson, started April 2004. +// +// Copyright (C) 2004 Curtis L. Olson - curt@flightgear.org +// +// 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ + + +#include +#include + +#include +#include +#include + +#include "sample_openal.hxx" + + +// +// SGSoundSample +// + + +static void print_openal_error( ALuint error ) { + if ( error == AL_INVALID_NAME ) { + SG_LOG( SG_GENERAL, SG_ALERT, "AL_INVALID_NAME" ); + } else if ( error == AL_ILLEGAL_ENUM ) { + SG_LOG( SG_GENERAL, SG_ALERT, "AL_ILLEGAL_ENUM" ); + } else if ( error == AL_INVALID_VALUE ) { + SG_LOG( SG_GENERAL, SG_ALERT, "AL_INVALID_VALUE" ); + } else if ( error == AL_ILLEGAL_COMMAND ) { + SG_LOG( SG_GENERAL, SG_ALERT, "AL_ILLEGAL_COMMAND" ); + } else if ( error == AL_OUT_OF_MEMORY ) { + SG_LOG( SG_GENERAL, SG_ALERT, "AL_OUT_OF_MEMORY" ); + } else { + SG_LOG( SG_GENERAL, SG_ALERT, "Unhandled error code = " << error ); + } +} + + +// constructor +SGSoundSample::SGSoundSample( const char *path, const char *file ) : + pitch(1.0), + volume(1.0), + loop(AL_FALSE) +{ + SGPath samplepath( path ); + if ( strlen(file) ) { + samplepath.append( file ); + } + + SG_LOG( SG_GENERAL, SG_ALERT, "From file sounds sample = " + << samplepath.str() ); + + ALuint error; + + source_pos[0] = 0.0; source_pos[1] = 0.0; source_pos[2] = 0.0; + source_vel[0] = 0.0; source_vel[1] = 0.0; source_vel[2] = 0.0; + + // clear errors from elsewhere? + alGetError(); + + // create an OpenAL buffer handle + alGenBuffers(1, &buffer); + error = alGetError(); + if ( error != AL_NO_ERROR ) { + print_openal_error( error ); + throw sg_exception("Failed to gen OpenAL buffer."); + } else { + SG_LOG( SG_GENERAL, SG_ALERT, "Buffer created ok!" ); + } + + // Load the sample file + alutLoadWAVFile( (ALbyte *)samplepath.c_str(), + &format, &data, &size, &freq, &loop ); + if (alGetError() != AL_NO_ERROR) { + throw sg_exception("Failed to load wav file."); + } + + // Copy data to the internal OpenAL buffer + alBufferData( buffer, format, data, size, freq ); + if (alGetError() != AL_NO_ERROR) { + throw sg_exception("Failed to buffer data."); + } + + alutUnloadWAV( format, data, size, freq ); + + // Bind buffer with a source. + alGenSources(1, &source); + if (alGetError() != AL_NO_ERROR) { + throw sg_exception("Failed to gen source."); + } + + alSourcei( source, AL_BUFFER, buffer ); + alSourcef( source, AL_PITCH, pitch ); + alSourcef( source, AL_GAIN, volume ); + alSourcefv( source, AL_POSITION, source_pos ); + alSourcefv( source, AL_VELOCITY, source_vel ); + alSourcei( source, AL_LOOPING, loop ); +} + + +// constructor +SGSoundSample::SGSoundSample( unsigned char *_data, int len, int _freq ) : + pitch(1.0), + volume(1.0), + loop(AL_FALSE) +{ + SG_LOG( SG_GENERAL, SG_ALERT, "In memory sounds sample" ); + + source_pos[0] = 0.0; source_pos[1] = 0.0; source_pos[2] = 0.0; + source_vel[0] = 0.0; source_vel[1] = 0.0; source_vel[2] = 0.0; + + // Load wav data into a buffer. + alGenBuffers(1, &buffer); + if (alGetError() != AL_NO_ERROR) { + SG_LOG( SG_GENERAL, SG_ALERT, "Error in alGenBuffers()" ); + return; + } + + format = AL_FORMAT_MONO8; + size = len; + data = _data; + freq = _freq; + + alBufferData( buffer, format, data, size, freq ); + + // Bind buffer with a source. + alGenSources(1, &source); + if (alGetError() != AL_NO_ERROR) { + throw sg_exception("Failed to gen source."); + } + + alSourcei( source, AL_BUFFER, buffer ); + alSourcef( source, AL_PITCH, pitch ); + alSourcef( source, AL_GAIN, volume ); + alSourcefv( source, AL_POSITION, source_pos ); + alSourcefv( source, AL_VELOCITY, source_vel ); + alSourcei( source, AL_LOOPING, loop ); +} + + +// destructor +SGSoundSample::~SGSoundSample() { + SG_LOG( SG_GENERAL, SG_ALERT, "Deleting a sample" ); + alDeleteSources(1, &source); + alDeleteBuffers(1, &buffer); +} + + +// play the sample +void SGSoundSample::play( bool _loop ) { + loop = _loop; + + // make sure sound isn't already playing + alSourceStop( source ); + + alSourcei( source, AL_LOOPING, loop ); + alSourcePlay( source ); +} + + +// stop playing the sample +void SGSoundSample::stop() { + alSourceStop( source ); +} diff --git a/simgear/sound/sample_openal.hxx b/simgear/sound/sample_openal.hxx new file mode 100644 index 00000000..fe79cb68 --- /dev/null +++ b/simgear/sound/sample_openal.hxx @@ -0,0 +1,170 @@ +// sample.hxx -- Sound sample encapsulation class +// +// Written by Curtis Olson, started April 2004. +// +// Copyright (C) 2004 Curtis L. Olson - curt@flightgear.org +// +// 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ + +/** + * \file sample.hxx + * Provides a sound sample encapsulation + */ + +#ifndef _SG_SAMPLE_HXX +#define _SG_SAMPLE_HXX 1 + +#ifndef __cplusplus +# error This library requires C++ +#endif + +#include + +#include + +#include + + +/** + * manages everything we need to know for an individual sound sample + */ + +class SGSoundSample { + +private: + + // Buffers hold sound data. + ALuint buffer; + + // Sources are points emitting sound. + ALuint source; + + // Position of the source sound. + ALfloat source_pos[3]; + + // Velocity of the source sound. + ALfloat source_vel[3]; + + // configuration values + ALenum format; + ALsizei size; + ALvoid* data; + ALsizei freq; + + double pitch; + double volume; + ALboolean loop; + +public: + + SGSoundSample( const char *path, const char *file ); + SGSoundSample( unsigned char *_data, int len, int _freq ); + ~SGSoundSample(); + + /** + * Start playing this sample. + * + * @param looped Define wether the sound should be played in a loop. + */ + void play( bool _loop ); + + /** + * Stop playing this sample. + * + * @param sched A pointer to the appropriate scheduler. + */ + void stop(); + + /** + * Play this sample once. + * @see #play + */ + inline void play_once() { play(false); } + + /** + * Play this sample looped. + * @see #play + */ + inline void play_looped() { play(true); } + + /** + * Test if a sample is curretnly playing. + * @return true if is is playing, false otherwise. + */ + inline bool is_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()!" ); + } + return (result == AL_PLAYING) ; + } + + /** + * Get the current pitch setting of this sample. + */ + inline double get_pitch() const { return pitch; } + + /** + * Set the pitch of this sample. + */ + inline void set_pitch( double p ) { + pitch = p; + alSourcef( source, AL_PITCH, pitch ); + if ( alGetError() != AL_NO_ERROR) { + SG_LOG( SG_GENERAL, SG_ALERT, + "Oops AL error in sample set_pitch()! " << p ); + } + } + + /** + * Get the current volume setting of this sample. + */ + inline double get_volume() const { return volume; } + + /** + * Set the volume of this sample. + */ + inline void set_volume( double v ) { + volume = v; + alSourcef( source, AL_GAIN, volume ); + if ( alGetError() != AL_NO_ERROR) { + SG_LOG( SG_GENERAL, SG_ALERT, + "Oops AL error in sample set_volume()!" ); + } + } + + /** + * Returns the size of the sounds sample + */ + inline int get_size() { + return size; + } + + /** + * Return a pointer to the raw data + */ + inline char *get_data() { + return (char *)data; + } +}; + + +#endif // _SG_SAMPLE_HXX + + diff --git a/simgear/sound/sound.cxx b/simgear/sound/sound.cxx deleted file mode 100644 index 8ceaf182..00000000 --- a/simgear/sound/sound.cxx +++ /dev/null @@ -1,409 +0,0 @@ -// sound.cxx -- Sound class implementation -// -// Started by Erik Hofman, February 2002 -// (Reuses some code from fg_fx.cxx created by David Megginson) -// -// Copyright (C) 2002 Curtis L. Olson - curt@flightgear.org -// -// 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., 675 Mass Ave, Cambridge, MA 02139, USA. -// -// $Id$ - -#include - -#ifdef SG_HAVE_STD_INCLUDES -# include -#else -# include -#endif -#include - -#include -#include -#include - - -#include "sound.hxx" - - -// static double _snd_lin(double v) { return v; } -static double _snd_inv(double v) { return (v == 0) ? 1e99 : 1/v; } -static double _snd_abs(double v) { return (v >= 0) ? v : -v; } -static double _snd_sqrt(double v) { return (v < 0) ? sqrt(-v) : sqrt(v); } -static double _snd_log10(double v) { return (v < 1) ? 0 : fast_log10(v); } -static double _snd_log(double v) { return (v < 1) ? 0 : fast_log(v); } -// static double _snd_sqr(double v) { return v*v; } -// static double _snd_pow3(double v) { return v*v*v; } - -static const struct { - 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} -}; - -SGSound::SGSound() - : _sample(NULL), - _condition(NULL), - _property(NULL), - _active(false), - _name(""), - _mode(SGSound::ONCE), - _prev_value(0), - _dt_play(0.0), - _dt_stop(0.0), - _stopping(0.0) -{ -} - -SGSound::~SGSound() -{ - _mgr->get_scheduler()->stopSample(_sample->get_sample()); - - if (_property) - delete _property; - - if (_condition) - delete _condition; - - _volume.clear(); - _pitch.clear(); - delete _sample; -} - -void -SGSound::init(SGPropertyNode *root, SGPropertyNode *node, SGSoundMgr *sndmgr, - const string &path) -{ - - // - // set global sound properties - // - - _name = node->getStringValue("name", ""); - SG_LOG(SG_GENERAL, SG_INFO, "Loading sound information for: " << _name ); - - const char *mode_str = node->getStringValue("mode", ""); - if ( !strcmp(mode_str, "looped") ) { - _mode = SGSound::LOOPED; - - } else if ( !strcmp(mode_str, "in-transit") ) { - _mode = SGSound::IN_TRANSIT; - - } else { - _mode = SGSound::ONCE; - - if ( strcmp(mode_str, "") ) - SG_LOG(SG_GENERAL,SG_INFO, " Unknown sound mode, default to 'once'"); - } - - _property = root->getNode(node->getStringValue("property", ""), true); - SGPropertyNode *condition = node->getChild("condition"); - if (condition != NULL) - _condition = sgReadCondition(root, condition); - - if (!_property && !_condition) - SG_LOG(SG_GENERAL, SG_WARN, - " Neither a condition nor a property specified"); - - // - // set volume properties - // - unsigned int i; - float v = 0.0; - vector kids = node->getChildren("volume"); - for (i = 0; (i < kids.size()) && (i < SGSound::MAXPROP); i++) { - _snd_prop volume = {NULL, NULL, NULL, 1.0, 0.0, 0.0, 0.0, false}; - - if (strcmp(kids[i]->getStringValue("property"), "")) - volume.prop = root->getNode(kids[i]->getStringValue("property", ""), true); - - const char *intern_str = kids[i]->getStringValue("internal", ""); - if (!strcmp(intern_str, "dt_play")) - volume.intern = &_dt_play; - else if (!strcmp(intern_str, "dt_stop")) - volume.intern = &_dt_stop; - - if ((volume.factor = kids[i]->getDoubleValue("factor", 1.0)) != 0.0) - if (volume.factor < 0.0) { - volume.factor = -volume.factor; - volume.subtract = true; - } - - const char *type_str = kids[i]->getStringValue("type", ""); - if ( strcmp(type_str, "") ) { - - for (int j=0; __sound_fn[j].fn; j++) - if ( !strcmp(type_str, __sound_fn[j].name) ) { - volume.fn = __sound_fn[j].fn; - break; - } - - if (!volume.fn) - SG_LOG(SG_GENERAL,SG_INFO, - " Unknown volume type, default to 'lin'"); - } - - volume.offset = kids[i]->getDoubleValue("offset", 0.0); - - if ((volume.min = kids[i]->getDoubleValue("min", 0.0)) < 0.0) - SG_LOG( SG_GENERAL, SG_WARN, - "Volume minimum value below 0. Forced to 0."); - - volume.max = kids[i]->getDoubleValue("max", 0.0); - if (volume.max && (volume.max < volume.min) ) - SG_LOG(SG_GENERAL,SG_ALERT, - " Volume maximum below minimum. Neglected."); - - _volume.push_back(volume); - v += volume.offset; - - } - - - // - // set pitch properties - // - float p = 0.0; - kids = node->getChildren("pitch"); - for (i = 0; (i < kids.size()) && (i < SGSound::MAXPROP); i++) { - _snd_prop pitch = {NULL, NULL, NULL, 1.0, 1.0, 0.0, 0.0, false}; - - if (strcmp(kids[i]->getStringValue("property", ""), "")) - pitch.prop = root->getNode(kids[i]->getStringValue("property", ""), true); - - const char *intern_str = kids[i]->getStringValue("internal", ""); - if (!strcmp(intern_str, "dt_play")) - pitch.intern = &_dt_play; - else if (!strcmp(intern_str, "dt_stop")) - pitch.intern = &_dt_stop; - - if ((pitch.factor = kids[i]->getDoubleValue("factor", 1.0)) != 0.0) - if (pitch.factor < 0.0) { - pitch.factor = -pitch.factor; - pitch.subtract = true; - } - - const char *type_str = kids[i]->getStringValue("type", ""); - if ( strcmp(type_str, "") ) { - - for (int j=0; __sound_fn[j].fn; j++) - if ( !strcmp(type_str, __sound_fn[j].name) ) { - pitch.fn = __sound_fn[j].fn; - break; - } - - if (!pitch.fn) - SG_LOG(SG_GENERAL,SG_INFO, - " Unknown pitch type, default to 'lin'"); - } - - pitch.offset = kids[i]->getDoubleValue("offset", 1.0); - - if ((pitch.min = kids[i]->getDoubleValue("min", 0.0)) < 0.0) - SG_LOG(SG_GENERAL,SG_WARN, - " Pitch minimum value below 0. Forced to 0."); - - pitch.max = kids[i]->getDoubleValue("max", 0.0); - if (pitch.max && (pitch.max < pitch.min) ) - SG_LOG(SG_GENERAL,SG_ALERT, - " Pitch maximum below minimum. Neglected"); - - _pitch.push_back(pitch); - p += pitch.offset; - } - - // - // Initialize the sample - // - _mgr = sndmgr; - if ((_sample = _mgr->find(_name)) == NULL) - _sample = _mgr->add(_name, path.c_str(), node->getStringValue("path", "")); - - _sample->set_volume(v); - _sample->set_pitch(p); -} - -void -SGSound::update (double dt) -{ - double curr_value = 0.0; - - // - // If the state changes to false, stop playing. - // - if (_property) - curr_value = _property->getDoubleValue(); - - if ( // Lisp, anyone? - (_condition && !_condition->test()) || - (!_condition && _property && - ( - !curr_value || - ( (_mode == SGSound::IN_TRANSIT) && (curr_value == _prev_value) ) - ) - ) - ) - { - if ((_mode != SGSound::IN_TRANSIT) || (_stopping > MAX_TRANSIT_TIME)) { - if (_sample->is_playing()) { - SG_LOG(SG_GENERAL, SG_INFO, "Stopping audio after " << _dt_play - << " sec: " << _name ); - - _sample->stop( _mgr->get_scheduler() ); - } - - _active = false; - _dt_stop += dt; - _dt_play = 0.0; - } else { - _stopping += dt; - } - - return; - } - - // - // If the mode is ONCE and the sound is still playing, - // we have nothing to do anymore. - // - if (_active && (_mode == SGSound::ONCE)) { - - if (!_sample->is_playing()) { - _dt_stop += dt; - _dt_play = 0.0; - } else { - _dt_play += dt; - } - - return; - } - - // - // Update the playing time, cache the current value and - // clear the delay timer. - // - _dt_play += dt; - _prev_value = curr_value; - _stopping = 0.0; - - // - // Update the volume - // - int i; - int max = _volume.size(); - double volume = 1.0; - double volume_offset = 0.0; - - for(i = 0; i < max; i++) { - double v = 1.0; - - if (_volume[i].prop) - v = _volume[i].prop->getDoubleValue(); - - else if (_volume[i].intern) - v = *_volume[i].intern; - - if (_volume[i].fn) - v = _volume[i].fn(v); - - v *= _volume[i].factor; - - if (_volume[i].max && (v > _volume[i].max)) - v = _volume[i].max; - - else if (v < _volume[i].min) - v = _volume[i].min; - - if (_volume[i].subtract) // Hack! - volume = _volume[i].offset - v; - - else { - volume_offset += _volume[i].offset; - volume *= v; - } - } - - // - // Update the pitch - // - max = _pitch.size(); - double pitch = 1.0; - double pitch_offset = 0.0; - - for(i = 0; i < max; i++) { - double p = 1.0; - - if (_pitch[i].prop) - p = _pitch[i].prop->getDoubleValue(); - - else if (_pitch[i].intern) - p = *_pitch[i].intern; - - if (_pitch[i].fn) - p = _pitch[i].fn(p); - - p *= _pitch[i].factor; - - if (_pitch[i].max && (p > _pitch[i].max)) - p = _pitch[i].max; - - else if (p < _pitch[i].min) - p = _pitch[i].min; - - if (_pitch[i].subtract) // Hack! - pitch = _pitch[i].offset - p; - - else { - pitch_offset += _pitch[i].offset; - pitch *= p; - } - } - - // - // Change sample state - // - _sample->set_pitch( pitch_offset + pitch ); - _sample->set_volume( volume_offset + volume ); - - - // - // Do we need to start playing the sample? - // - if (!_active) { - - if (_mode == SGSound::ONCE) - _sample->play(_mgr->get_scheduler(), false); - - else - _sample->play(_mgr->get_scheduler(), true); - - SG_LOG(SG_GENERAL, SG_INFO, "Playing audio after " << _dt_stop - << " sec: " << _name); - SG_LOG(SG_GENERAL, SG_BULK, - "Playing " << ((_mode == ONCE) ? "once" : "looped")); - - _active = true; - _dt_stop = 0.0; - } -} diff --git a/simgear/sound/sound.hxx b/simgear/sound/sound.hxx deleted file mode 100644 index 48888b0a..00000000 --- a/simgear/sound/sound.hxx +++ /dev/null @@ -1,154 +0,0 @@ -// sound.hxx -- Sound class implementation -// -// Started by Erik Hofman, February 2002 -// -// Copyright (C) 2002 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., 675 Mass Ave, Cambridge, MA 02139, USA. -// -// $Id$ - -/** - * \file sound.hxx - * Provides a class to manage a single sound event including things - * like looping, volume and pitch changes. - */ - -#ifndef _SG_SOUND_HXX -#define _SG_SOUND_HXX 1 - -#ifndef __cplusplus -# error This library requires C++ -#endif - -#include -#include - -#include "soundmgr.hxx" - -static const double MAX_TRANSIT_TIME = 0.1; // 100 ms. - - -/** - * Class for handling one sound event. - * - * This class handles everything for a particular sound event, by - * scanning an a pre-loaded property tree structure for sound - * settings, setting up its internal states, and managing sound - * playback whenever such an event happens. - */ -class SGSound -{ - -public: - - SGSound(); - virtual ~SGSound(); - - /** - * Initialize the sound event. - * - * Prior to initialization of the sound event the propgrams property root - * has to be defined, the sound configuration XML tree has to be loaded - * and a sound manager class has to be defined. - * - * A sound configuration file would look like this: - * - * - * Define the name of the event. For refference only. - * Either: - * looped: play this sound looped. - * in-transit: play looped while the event is happening. - * once: play this sound once. - * The relative path to the audio file. - * Take action if this property becomes true. - * Take action if this condition becomes true. - * or Define volume or pitch settings. - * Take the value of this property as a refference for the - * result. - * Either: - * dt_start: the time elapsed since this sound is playing. - * dt_stop: the time elapsed since this sound has stopped. - * Add this value to the result. - * Multiply the result by this factor. - * Make sure the value is never less than this value. - * Make sure the value is never larger than this value. - * or - * - * - * - * - * - * - * @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 path The path where the audio files remain. - */ - virtual void init (SGPropertyNode *, SGPropertyNode *, SGSoundMgr *, - const string &); - - /** - * Check wheter an event has happened and if action has to be taken. - */ - virtual void update (double dt); - - /** - * Stop taking action on the pre-defined events. - */ - void stop(); - -protected: - - enum { MAXPROP=5 }; - enum { ONCE=0, LOOPED, IN_TRANSIT }; - enum { LEVEL=0, INVERTED, FLIPFLOP }; - - // SGSound properties - typedef struct { - SGPropertyNode * prop; - double (*fn)(double); - double *intern; - double factor; - double offset; - double min; - double max; - bool subtract; - } _snd_prop; - -private: - - SGSoundMgr * _mgr; - SGSimpleSound * _sample; - - SGCondition * _condition; - SGPropertyNode * _property; - - bool _active; - string _name; - int _mode; - double _prev_value; - double _dt_play; - double _dt_stop; - double _stopping; // time after the sound should have stopped. - // This is usefull for lost packets in in-trasit mode. - - vector<_snd_prop> _volume; - vector<_snd_prop> _pitch; - -}; - -#endif // _SG_SOUND_HXX diff --git a/simgear/sound/soundmgr.cxx b/simgear/sound/soundmgr.cxx deleted file mode 100644 index 31065656..00000000 --- a/simgear/sound/soundmgr.cxx +++ /dev/null @@ -1,416 +0,0 @@ -// soundmgr.cxx -- Sound effect management class -// -// Sound manager initially written by David Findlay -// 2001 -// -// C++-ified by Curtis Olson, started March 2001. -// -// Copyright (C) 2001 Curtis L. Olson - curt@flightgear.org -// -// 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., 675 Mass Ave, Cambridge, MA 02139, USA. -// -// $Id$ - -#include - -#include -#include - -#include "soundmgr.hxx" - -#define SOUND_SAFETY_MULT 3 -#define MAX_SOUND_SAFETY ( 1.0 / SOUND_SAFETY_MULT ) - -// -// SGSimpleSound -// - -// constructor -SGSimpleSound::SGSimpleSound( const char *path, const char *file ) - : sample(NULL), - pitch_envelope(NULL), - volume_envelope(NULL), - pitch(1.0), - volume(1.0) -{ - SGPath slfile( path ); - if ( file ) - slfile.append( file ); - - sample = new slSample ( slfile.c_str() ); - pitch_envelope = new slEnvelope( 1, SL_SAMPLE_ONE_SHOT ); - volume_envelope = new slEnvelope( 1, SL_SAMPLE_ONE_SHOT ); - pitch_envelope->setStep ( 0, 0.01, 1.0 ); - volume_envelope->setStep ( 0, 0.01, 1.0 ); -} - -SGSimpleSound::SGSimpleSound( unsigned char *buffer, int len ) - : pitch(1.0), - volume(1.0) -{ - sample = new slSample ( buffer, len ); - pitch_envelope = new slEnvelope( 1, SL_SAMPLE_ONE_SHOT ); - volume_envelope = new slEnvelope( 1, SL_SAMPLE_ONE_SHOT ); - pitch_envelope->setStep ( 0, 0.01, 1.0 ); - volume_envelope->setStep ( 0, 0.01, 1.0 ); -} - -// destructor -SGSimpleSound::~SGSimpleSound() { - delete pitch_envelope; - delete volume_envelope; - delete sample; -} - -void SGSimpleSound::play( slScheduler *sched, bool looped ) { - - // make sure sound isn't already playing - if ( sample->getPlayCount() > 0 ) { - sched->stopSample(sample); - // return; - } - - if ( looped ) { - sched->loopSample(sample); - } else { - sched->playSample(sample); - } - - sched->addSampleEnvelope(sample, 0, 0, pitch_envelope, SL_PITCH_ENVELOPE); - sched->addSampleEnvelope(sample, 0, 1, volume_envelope, SL_VOLUME_ENVELOPE); -} - -void SGSimpleSound::stop( slScheduler *sched ) { - - sched->stopSample( sample ); -} - -// -// Sound Manager -// - -// constructor -SGSoundMgr::SGSoundMgr() { - audio_sched = new slScheduler( 8000 ); - if ( audio_sched->notWorking() ) { - SG_LOG( SG_GENERAL, SG_ALERT, "Audio initialization failed!" ); - } else { - audio_sched -> setMaxConcurrent ( SL_MAX_MIXERINPUTS ); - - audio_mixer = new smMixer; - - SG_LOG( SG_GENERAL, SG_INFO, - "Rate = " << audio_sched->getRate() - << " Bps = " << audio_sched->getBps() - << " Stereo = " << audio_sched->getStereo() ); - } -} - -// destructor - -SGSoundMgr::~SGSoundMgr() { - - // - // Remove the samples from the sample manager. - // - sample_map_iterator sample_current = samples.begin(); - sample_map_iterator sample_end = samples.end(); - for ( ; sample_current != sample_end; ++sample_current ) { - sample_ref *sr = sample_current->second; - - audio_sched->stopSample(sr->sample); - delete sr->sample; - delete sr; - } - - // - // Remove the sounds from the sound manager. - // - sound_map_iterator sound_current = sounds.begin(); - sound_map_iterator sound_end = sounds.end(); - for ( ; sound_current != sound_end; ++sound_current ) { - SGSimpleSound *s = sound_current->second; - - audio_sched->stopSample(s->get_sample()); - delete s->get_sample(); - delete s; - } - - delete audio_sched; - delete audio_mixer; -} - - -// initialize the sound manager -void SGSoundMgr::init() { - safety = MAX_SOUND_SAFETY; - - // audio_mixer -> setMasterVolume ( 80 ) ; /* 80% of max volume. */ - audio_sched -> setSafetyMargin ( SOUND_SAFETY_MULT * safety ) ; - - - // - // Remove the samples from the sample manager. - // - sample_map_iterator sample_current = samples.begin(); - sample_map_iterator sample_end = samples.end(); - for ( ; sample_current != sample_end; ++sample_current ) { - sample_ref *sr = sample_current->second; - - audio_sched->stopSample(sr->sample); - delete sr->sample; - delete sr; - } - samples.clear(); - - // - // Remove the sounds from the sound manager. - // - sound_map_iterator sound_current = sounds.begin(); - sound_map_iterator sound_end = sounds.end(); - for ( ; sound_current != sound_end; ++sound_current ) { - SGSimpleSound *s = sound_current->second; - - audio_sched->stopSample(s->get_sample()); - delete s->get_sample(); - delete s; - } - sounds.clear(); - -} - - -void SGSoundMgr::bind () -{ - // no properties yet -} - - -void SGSoundMgr::unbind () -{ - // no properties yet -} - - -// run the audio scheduler -void SGSoundMgr::update( double dt ) { - if ( dt > safety ) { - safety = dt; - } else { - safety = safety * 0.99 + dt * 0.01; - } - if ( safety > MAX_SOUND_SAFETY ) { - safety = MAX_SOUND_SAFETY; - } - // cout << "safety = " << safety << endl; - audio_sched -> setSafetyMargin ( SOUND_SAFETY_MULT * safety ) ; - - if ( !audio_sched->not_working() ) - audio_sched -> update(); -} - - -void -SGSoundMgr::pause () -{ - audio_sched->pauseSample(0, 0); -} - - -void -SGSoundMgr::resume () -{ - audio_sched->resumeSample(0, 0); -} - - -// add a sound effect, return true if successful -bool SGSoundMgr::add( SGSimpleSound *sound, const string& refname ) { - - sound_map_iterator sound_it = sounds.find( refname ); - if ( sound_it != sounds.end() ) { - // sound already exists - return false; - } - - sample_map_iterator sample_it = samples.find( refname ); - if ( sample_it != samples.end() ) { - // this sound has existed in the past and it's sample is still - // here, delete the sample so we can replace it. - samples.erase( sample_it ); - } - - sample_ref *sr = new sample_ref; - - sr->n=1; - sr->sample = sound->get_sample(); - samples[refname] = sr; - - sounds[refname] = sound; - - return true; -} - - -// add a sound from a file, return the sample if successful, else return NULL -SGSimpleSound *SGSoundMgr::add( const string &refname, - const char *path, const char *file ) { - SGSimpleSound *sound; - - SGPath slfile( path ); - if ( file ) - slfile.append( file ); - - if ( slfile.str().empty() ) - return NULL; - - sample_map_iterator it = samples.find(slfile.str()); - if (it == samples.end()) { - - sound = new SGSimpleSound(slfile.c_str()); - sounds[refname] = sound; - - sample_ref *sr = new sample_ref; - - sr->n=1; - sr->sample = sound->get_sample(); - samples[slfile.str()] = sr; - - } else { - sample_ref *sr = it->second; - - sr->n++; - sound = - new SGSimpleSound(sr->sample->getBuffer(), sr->sample->getLength()); - sounds[refname] = sound; - - } - - return sound; -} - - -// remove a sound effect, return true if successful -bool SGSoundMgr::remove( const string &refname ) { - - sound_map_iterator it = sounds.find( refname ); - if ( it != sounds.end() ) { - // first stop the sound from playing (so we don't bomb the - // audio scheduler) - SGSimpleSound *sample = it->second; - - // cout << "Playing " << sample->get_sample()->getPlayCount() - // << " instances!" << endl; - - audio_sched->stopSample( sample->get_sample() ); - audio_sched->addSampleEnvelope( sample->get_sample(), 0, 0, - NULL, - SL_PITCH_ENVELOPE ); - audio_sched->addSampleEnvelope( sample->get_sample(), 0, 1, - NULL, - SL_VOLUME_ENVELOPE ); - - // must call audio_sched->update() after stopping the sound - // but before deleting it. - audio_sched -> update(); - // cout << "Still playing " << sample->get_sample()->getPlayCount() - // << " instances!" << endl; - - // - // FIXME: - // Due to the change in the sound manager, samples live - // until the sound manager gets removed. - // - // delete sample; - sounds.erase( it ); - - // cout << "sndmgr: removed -> " << refname << endl; - return true; - } else { - // cout << "sndmgr: failed remove -> " << refname << endl; - return false; - } -} - - -// return true of the specified sound exists in the sound manager system -bool SGSoundMgr::exists( const string &refname ) { - sound_map_iterator it = sounds.find( refname ); - if ( it != sounds.end() ) { - return true; - } else { - return false; - } -} - - -// return a pointer to the SGSimpleSound if the specified sound exists -// in the sound manager system, otherwise return NULL -SGSimpleSound *SGSoundMgr::find( const string &refname ) { - sound_map_iterator it = sounds.find( refname ); - if ( it != sounds.end() ) { - return it->second; - } else { - return NULL; - } -} - - -// tell the scheduler to play the indexed sample in a continuous -// loop -bool SGSoundMgr::play_looped( const string &refname ) { - SGSimpleSound *sample; - - if ((sample = find( refname )) == NULL) - return false; - - sample->play(audio_sched, true); - return true; -} - - -// tell the scheduler to play the indexed sample once -bool SGSoundMgr::play_once( const string& refname ) { - SGSimpleSound *sample; - - if ((sample = find( refname )) == NULL) - return false; - - sample->play(audio_sched, false); - return true; -} - - -// return true of the specified sound is currently being played -bool SGSoundMgr::is_playing( const string& refname ) { - SGSimpleSound *sample; - - if ((sample = find( refname )) == NULL) - return false; - - return (sample->get_sample()->getPlayCount() > 0 ); -} - - -// immediate stop playing the sound -bool SGSoundMgr::stop( const string& refname ) { - SGSimpleSound *sample; - - if ((sample = find( refname )) == NULL) - return false; - - audio_sched->stopSample( sample->get_sample() ); - return true; -} diff --git a/simgear/sound/soundmgr.hxx b/simgear/sound/soundmgr.hxx deleted file mode 100644 index 852fdc02..00000000 --- a/simgear/sound/soundmgr.hxx +++ /dev/null @@ -1,296 +0,0 @@ -// soundmgr.hxx -- Sound effect management class -// -// Sound manager initially written by David Findlay -// 2001 -// -// C++-ified by Curtis Olson, started March 2001. -// -// Copyright (C) 2001 Curtis L. Olson - curt@flightgear.org -// -// 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., 675 Mass Ave, Cambridge, MA 02139, USA. -// -// $Id$ - -/** - * \file soundmgr.hxx - * Provides a sound manager class to keep track of - * multiple sounds and manage playing them with different effects and - * timings. - */ - -#ifndef _SG_SOUNDMGR_HXX -#define _SG_SOUNDMGR_HXX 1 - -#ifndef __cplusplus -# error This library requires C++ -#endif - -#include -#include - -#include STL_STRING -#include - -#include -#include - -SG_USING_STD(map); -SG_USING_STD(string); - - -/** - * manages everything we need to know for an individual sound sample - */ -class SGSimpleSound { - -private: - - slSample *sample; - slEnvelope *pitch_envelope; - slEnvelope *volume_envelope; - double pitch; - double volume; - -public: - - SGSimpleSound( const char *path, const char *file = NULL ); - SGSimpleSound( unsigned char *buffer, int len ); - ~SGSimpleSound(); - - /** - * Start playing this sample. - * - * @param sched A pointer to the appropriate scheduler. - * @param looped Define wether the sound should be played in a loop. - */ - void play( slScheduler *sched, bool looped ); - - /** - * Stop playing this sample. - * - * @param sched A pointer to the appropriate scheduler. - */ - void stop( slScheduler *sched ); - - /** - * Play this sample once. - * @see #play - */ - inline void play_once( slScheduler *sched ) { play( sched, false); } - - /** - * Play this sample looped. - * @see #play - */ - inline void play_looped( slScheduler *sched ) { play( sched, true); } - - /** - * Test if a sample is curretnly playing. - * @return true if is is playing, false otherwise. - */ - inline bool is_playing( ) { - return ( sample->getPlayCount() > 0 ); - } - - /** - * Get the current pitch setting of this sample. - */ - inline double get_pitch() const { return pitch; } - - /** - * Set the pitch of this sample. - */ - inline void set_pitch( double p ) { - pitch = p; - pitch_envelope->setStep( 0, 0.01, pitch ); - } - - /** - * Get the current volume setting of this sample. - */ - inline double get_volume() const { return volume; } - - /** - * Set the volume of this sample. - */ - inline void set_volume( double v ) { - volume = v; - volume_envelope->setStep( 0, 0.01, volume ); - } - - /** - * Get a refference to the raw sample. - */ - inline slSample *get_sample() { return sample; } - - /** - * Get the pitch envelope setting of this sample. - */ - inline slEnvelope *get_pitch_envelope() { return pitch_envelope; } - - /** - * Get the volume envelope setting of this sample. - */ - inline slEnvelope *get_volume_envelope() { return volume_envelope; } -}; - - -typedef struct { - int n; - slSample *sample; -} sample_ref; - -typedef map < string, sample_ref * > sample_map; -typedef sample_map::iterator sample_map_iterator; -typedef sample_map::const_iterator const_sample_map_iterator; - -typedef map < string, SGSimpleSound * > sound_map; -typedef sound_map::iterator sound_map_iterator; -typedef sound_map::const_iterator const_sound_map_iterator; - - -/** - * Manage a collection of SGSimpleSound instances - */ -class SGSoundMgr -{ - - slScheduler *audio_sched; - smMixer *audio_mixer; - - sound_map sounds; - sample_map samples; - - 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 resume (); - - - /** - * is audio working? - */ - inline bool is_working() const { return !audio_sched->notWorking(); } - - /** - * reinitialize the sound manager - */ - inline void reinit() { init(); } - - /** - * add a sound effect, return true if successful - */ - bool add( SGSimpleSound *sound, const string& refname); - - /** - * Add a sound file to the sound manager. - * - * The advantage of using this function over the previous one is that - * it doesn't load a sample if it already is in memory, but instead it - * uses the already loaded sample data. - * - * @param refname A refference name to make a distincion between samples. - * @param path The path or full filename of the sample to load. - * @param file An optional filename which will be appended to the path. - * @return An instance of the sound for further manipulation. - */ - SGSimpleSound *add( const string& refname, - const char *path, const char *file = NULL ); - - /** - * 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 SGSimpleSound if the specified sound - * exists in the sound manager system, otherwise return NULL - */ - SGSimpleSound *find( const string& refname ); - - /** - * tell the scheduler to play the indexed sample in a continuous - * loop - */ - bool play_looped( const string& refname ); - - /** - * 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 ); - - /** - * return the audio scheduler - */ - inline slScheduler *get_scheduler( ) { return audio_sched; }; -}; - - -#endif // _SG_SOUNDMGR_HXX - - diff --git a/simgear/sound/soundmgr_openal.cxx b/simgear/sound/soundmgr_openal.cxx new file mode 100644 index 00000000..2578f1cc --- /dev/null +++ b/simgear/sound/soundmgr_openal.cxx @@ -0,0 +1,263 @@ +// soundmgr.cxx -- Sound effect management class +// +// Sound manager initially written by David Findlay +// 2001 +// +// C++-ified by Curtis Olson, started March 2001. +// +// Copyright (C) 2001 Curtis L. Olson - curt@flightgear.org +// +// 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ + +#include + +#include +#include +#include + +#include +#include + +#include "soundmgr_openal.hxx" + + +// +// Sound Manager +// + +// constructor +SGSoundMgr::SGSoundMgr() { + + SG_LOG( SG_GENERAL, SG_ALERT, "Initializing OpenAL sound manager" ); + + // initialize OpenAL + alutInit( 0, NULL ); + alGetError(); + if ( alGetError() == AL_NO_ERROR) { + working = true; + } else { + working = false; + SG_LOG( SG_GENERAL, SG_ALERT, "Audio initialization failed!" ); + } + + 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; + + 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!" ); + } +} + +// destructor + +SGSoundMgr::~SGSoundMgr() { + + // + // Remove the samples from the sample manager. + // + 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; + delete sample; + } + + alutExit(); +} + + +// initialize the sound manager +void SGSoundMgr::init() { + // + // Remove the samples from the sample manager. + // + 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; + delete sample; + } + samples.clear(); +} + + +void SGSoundMgr::bind () +{ + // no properties +} + + +void SGSoundMgr::unbind () +{ + // no properties +} + + +// run the audio scheduler +void SGSoundMgr::update( double dt ) { +} + + +void +SGSoundMgr::pause () +{ + ALCcontext *pCurContext = alcGetCurrentContext(); + alcSuspendContext( pCurContext ); + if ( alGetError() != AL_NO_ERROR) { + SG_LOG( SG_GENERAL, SG_ALERT, + "Oops AL error after soundmgr pause()!" ); + } +} + + +void +SGSoundMgr::resume () +{ + ALCcontext *pCurContext = alcGetCurrentContext(); + alcProcessContext( pCurContext ); + if ( alGetError() != AL_NO_ERROR) { + SG_LOG( SG_GENERAL, SG_ALERT, + "Oops AL error after soundmgr 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 + return false; + } + + samples[refname] = sound; + + 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) + SGSoundSample *sample = sample_it->second; + delete sample; + samples.erase( sample_it ); + + // cout << "sndmgr: removed -> " << refname << endl; + return true; + } else { + // cout << "sndmgr: failed remove -> " << refname << endl; + return false; + } +} + + +// 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 { + return false; + } +} + + +// return a pointer to the SGSoundSample 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; + } +} + + +// 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; + } +} + + +// tell the scheduler to play the indexed sample once +bool SGSoundMgr::play_once( const string& refname ) { + SGSoundSample *sample; + + if ( (sample = find( refname )) == NULL ) { + return false; + } else { + sample->play( false ); + return true; + } +} + + +// return true of the specified sound is currently being played +bool SGSoundMgr::is_playing( const string& refname ) { + SGSoundSample *sample; + + if ( (sample = find( refname )) == NULL ) { + return false; + } else { + return ( sample->is_playing() ); + } +} + + +// immediate stop playing the sound +bool SGSoundMgr::stop( const string& refname ) { + SGSoundSample *sample; + + if ( (sample = find( refname )) == NULL ) { + return false; + } else { + sample->stop(); + return true; + } +} diff --git a/simgear/sound/soundmgr_openal.hxx b/simgear/sound/soundmgr_openal.hxx new file mode 100644 index 00000000..02b732d8 --- /dev/null +++ b/simgear/sound/soundmgr_openal.hxx @@ -0,0 +1,177 @@ +// soundmgr.hxx -- Sound effect management class +// +// Sound manager initially written by David Findlay +// 2001 +// +// C++-ified by Curtis Olson, started March 2001. +// +// Copyright (C) 2001 Curtis L. Olson - curt@flightgear.org +// +// 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ + +/** + * \file soundmgr.hxx + * Provides a sound manager class to keep track of + * multiple sounds and manage playing them with different effects and + * timings. + */ + +#ifndef _SG_SOUNDMGR_OPENAL_HXX +#define _SG_SOUNDMGR_OPENAL_HXX 1 + +#ifndef __cplusplus +# error This library requires C++ +#endif + +#include + +#include STL_STRING +#include + +#include + +#include "sample_openal.hxx" + +SG_USING_STD(map); +SG_USING_STD(string); + + +typedef map < string, SGSoundSample * > sample_map; +typedef sample_map::iterator sample_map_iterator; +typedef sample_map::const_iterator const_sample_map_iterator; + + +/** + * Manage a collection of SGSoundSample instances + */ +class SGSoundMgr +{ + + // 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 resume(); + + + /** + * is audio working? + */ + inline bool is_working() const { return working; } + + /** + * reinitialize the sound manager + */ + inline void reinit() { init(); } + + /** + * 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 ); + + /** + * tell the scheduler to play the indexed sample in a continuous + * loop + */ + bool play_looped( const string& refname ); + + /** + * 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 ); +}; + + +#endif // _SG_SOUNDMGR_OPENAL_HXX + + diff --git a/simgear/sound/xmlsound.cxx b/simgear/sound/xmlsound.cxx new file mode 100644 index 00000000..f9d6e3e5 --- /dev/null +++ b/simgear/sound/xmlsound.cxx @@ -0,0 +1,412 @@ +// sound.cxx -- Sound class implementation +// +// Started by Erik Hofman, February 2002 +// (Reuses some code from fg_fx.cxx created by David Megginson) +// +// Copyright (C) 2002 Curtis L. Olson - curt@flightgear.org +// +// 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ + +#include + +#ifdef SG_HAVE_STD_INCLUDES +# include +#else +# include +#endif +#include + +#include +#include +#include + + +#include "xmlsound.hxx" + + +// static double _snd_lin(double v) { return v; } +static double _snd_inv(double v) { return (v == 0) ? 1e99 : 1/v; } +static double _snd_abs(double v) { return (v >= 0) ? v : -v; } +static double _snd_sqrt(double v) { return (v < 0) ? sqrt(-v) : sqrt(v); } +static double _snd_log10(double v) { return (v < 1) ? 0 : fast_log10(v); } +static double _snd_log(double v) { return (v < 1) ? 0 : fast_log(v); } +// static double _snd_sqr(double v) { return v*v; } +// static double _snd_pow3(double v) { return v*v*v; } + +static const struct { + 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} +}; + +SGXmlSound::SGXmlSound() + : _sample(NULL), + _condition(NULL), + _property(NULL), + _active(false), + _name(""), + _mode(SGXmlSound::ONCE), + _prev_value(0), + _dt_play(0.0), + _dt_stop(0.0), + _stopping(0.0) +{ +} + +SGXmlSound::~SGXmlSound() +{ + _sample->stop(); + + if (_property) + delete _property; + + if (_condition) + delete _condition; + + _volume.clear(); + _pitch.clear(); + delete _sample; +} + +void +SGXmlSound::init(SGPropertyNode *root, SGPropertyNode *node, SGSoundMgr *sndmgr, + const string &path) +{ + + // + // set global sound properties + // + + _name = node->getStringValue("name", ""); + SG_LOG(SG_GENERAL, SG_INFO, "Loading sound information for: " << _name ); + + const char *mode_str = node->getStringValue("mode", ""); + if ( !strcmp(mode_str, "looped") ) { + _mode = SGXmlSound::LOOPED; + + } else if ( !strcmp(mode_str, "in-transit") ) { + _mode = SGXmlSound::IN_TRANSIT; + + } else { + _mode = SGXmlSound::ONCE; + + if ( strcmp(mode_str, "") ) + SG_LOG(SG_GENERAL,SG_INFO, " Unknown sound mode, default to 'once'"); + } + + _property = root->getNode(node->getStringValue("property", ""), true); + SGPropertyNode *condition = node->getChild("condition"); + if (condition != NULL) + _condition = sgReadCondition(root, condition); + + if (!_property && !_condition) + SG_LOG(SG_GENERAL, SG_WARN, + " Neither a condition nor a property specified"); + + // + // set volume properties + // + unsigned int i; + float v = 0.0; + vector kids = node->getChildren("volume"); + for (i = 0; (i < kids.size()) && (i < SGXmlSound::MAXPROP); i++) { + _snd_prop volume = {NULL, NULL, NULL, 1.0, 0.0, 0.0, 0.0, false}; + + if (strcmp(kids[i]->getStringValue("property"), "")) + volume.prop = root->getNode(kids[i]->getStringValue("property", ""), true); + + const char *intern_str = kids[i]->getStringValue("internal", ""); + if (!strcmp(intern_str, "dt_play")) + volume.intern = &_dt_play; + else if (!strcmp(intern_str, "dt_stop")) + volume.intern = &_dt_stop; + + if ((volume.factor = kids[i]->getDoubleValue("factor", 1.0)) != 0.0) + if (volume.factor < 0.0) { + volume.factor = -volume.factor; + volume.subtract = true; + } + + const char *type_str = kids[i]->getStringValue("type", ""); + if ( strcmp(type_str, "") ) { + + for (int j=0; __sound_fn[j].fn; j++) + if ( !strcmp(type_str, __sound_fn[j].name) ) { + volume.fn = __sound_fn[j].fn; + break; + } + + if (!volume.fn) + SG_LOG(SG_GENERAL,SG_INFO, + " Unknown volume type, default to 'lin'"); + } + + volume.offset = kids[i]->getDoubleValue("offset", 0.0); + + if ((volume.min = kids[i]->getDoubleValue("min", 0.0)) < 0.0) + SG_LOG( SG_GENERAL, SG_WARN, + "Volume minimum value below 0. Forced to 0."); + + volume.max = kids[i]->getDoubleValue("max", 0.0); + if (volume.max && (volume.max < volume.min) ) + SG_LOG(SG_GENERAL,SG_ALERT, + " Volume maximum below minimum. Neglected."); + + _volume.push_back(volume); + v += volume.offset; + + } + + + // + // set pitch properties + // + float p = 0.0; + kids = node->getChildren("pitch"); + for (i = 0; (i < kids.size()) && (i < SGXmlSound::MAXPROP); i++) { + _snd_prop pitch = {NULL, NULL, NULL, 1.0, 1.0, 0.0, 0.0, false}; + + if (strcmp(kids[i]->getStringValue("property", ""), "")) + pitch.prop = root->getNode(kids[i]->getStringValue("property", ""), true); + + const char *intern_str = kids[i]->getStringValue("internal", ""); + if (!strcmp(intern_str, "dt_play")) + pitch.intern = &_dt_play; + else if (!strcmp(intern_str, "dt_stop")) + pitch.intern = &_dt_stop; + + if ((pitch.factor = kids[i]->getDoubleValue("factor", 1.0)) != 0.0) + if (pitch.factor < 0.0) { + pitch.factor = -pitch.factor; + pitch.subtract = true; + } + + const char *type_str = kids[i]->getStringValue("type", ""); + if ( strcmp(type_str, "") ) { + + for (int j=0; __sound_fn[j].fn; j++) + if ( !strcmp(type_str, __sound_fn[j].name) ) { + pitch.fn = __sound_fn[j].fn; + break; + } + + if (!pitch.fn) + SG_LOG(SG_GENERAL,SG_INFO, + " Unknown pitch type, default to 'lin'"); + } + + pitch.offset = kids[i]->getDoubleValue("offset", 1.0); + + if ((pitch.min = kids[i]->getDoubleValue("min", 0.0)) < 0.0) + SG_LOG(SG_GENERAL,SG_WARN, + " Pitch minimum value below 0. Forced to 0."); + + pitch.max = kids[i]->getDoubleValue("max", 0.0); + if (pitch.max && (pitch.max < pitch.min) ) + SG_LOG(SG_GENERAL,SG_ALERT, + " Pitch maximum below minimum. Neglected"); + + _pitch.push_back(pitch); + p += pitch.offset; + } + + // + // Initialize the sample + // + _mgr = sndmgr; + if ( (_sample = _mgr->find(_name)) == NULL ) { + _sample = new SGSoundSample( path.c_str(), + node->getStringValue("path", "") ); + _mgr->add( _sample, _name ); + } + + _sample->set_volume(v); + _sample->set_pitch(p); +} + +void +SGXmlSound::update (double dt) +{ + double curr_value = 0.0; + + // + // If the state changes to false, stop playing. + // + if (_property) + curr_value = _property->getDoubleValue(); + + if ( // Lisp, anyone? + (_condition && !_condition->test()) || + (!_condition && _property && + ( + !curr_value || + ( (_mode == SGXmlSound::IN_TRANSIT) && (curr_value == _prev_value) ) + ) + ) + ) + { + if ((_mode != SGXmlSound::IN_TRANSIT) || (_stopping > MAX_TRANSIT_TIME)) { + if (_sample->is_playing()) { + SG_LOG(SG_GENERAL, SG_INFO, "Stopping audio after " << _dt_play + << " sec: " << _name ); + + _sample->stop(); + } + + _active = false; + _dt_stop += dt; + _dt_play = 0.0; + } else { + _stopping += dt; + } + + return; + } + + // + // If the mode is ONCE and the sound is still playing, + // we have nothing to do anymore. + // + if (_active && (_mode == SGXmlSound::ONCE)) { + + if (!_sample->is_playing()) { + _dt_stop += dt; + _dt_play = 0.0; + } else { + _dt_play += dt; + } + + return; + } + + // + // Update the playing time, cache the current value and + // clear the delay timer. + // + _dt_play += dt; + _prev_value = curr_value; + _stopping = 0.0; + + // + // Update the volume + // + int i; + int max = _volume.size(); + double volume = 1.0; + double volume_offset = 0.0; + + for(i = 0; i < max; i++) { + double v = 1.0; + + if (_volume[i].prop) + v = _volume[i].prop->getDoubleValue(); + + else if (_volume[i].intern) + v = *_volume[i].intern; + + if (_volume[i].fn) + v = _volume[i].fn(v); + + v *= _volume[i].factor; + + if (_volume[i].max && (v > _volume[i].max)) + v = _volume[i].max; + + else if (v < _volume[i].min) + v = _volume[i].min; + + if (_volume[i].subtract) // Hack! + volume = _volume[i].offset - v; + + else { + volume_offset += _volume[i].offset; + volume *= v; + } + } + + // + // Update the pitch + // + max = _pitch.size(); + double pitch = 1.0; + double pitch_offset = 0.0; + + for(i = 0; i < max; i++) { + double p = 1.0; + + if (_pitch[i].prop) + p = _pitch[i].prop->getDoubleValue(); + + else if (_pitch[i].intern) + p = *_pitch[i].intern; + + if (_pitch[i].fn) + p = _pitch[i].fn(p); + + p *= _pitch[i].factor; + + if (_pitch[i].max && (p > _pitch[i].max)) + p = _pitch[i].max; + + else if (p < _pitch[i].min) + p = _pitch[i].min; + + if (_pitch[i].subtract) // Hack! + pitch = _pitch[i].offset - p; + + else { + pitch_offset += _pitch[i].offset; + pitch *= p; + } + } + + // + // Change sample state + // + _sample->set_pitch( pitch_offset + pitch ); + _sample->set_volume( volume_offset + volume ); + + + // + // Do we need to start playing the sample? + // + if (!_active) { + + if (_mode == SGXmlSound::ONCE) + _sample->play(false); + + else + _sample->play(true); + + SG_LOG(SG_GENERAL, SG_INFO, "Playing audio after " << _dt_stop + << " sec: " << _name); + SG_LOG(SG_GENERAL, SG_BULK, + "Playing " << ((_mode == ONCE) ? "once" : "looped")); + + _active = true; + _dt_stop = 0.0; + } +} diff --git a/simgear/sound/xmlsound.hxx b/simgear/sound/xmlsound.hxx new file mode 100644 index 00000000..e290b393 --- /dev/null +++ b/simgear/sound/xmlsound.hxx @@ -0,0 +1,155 @@ +// sound.hxx -- Sound class implementation +// +// Started by Erik Hofman, February 2002 +// +// Copyright (C) 2002 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// +// $Id$ + +/** + * \file sound.hxx + * Provides a class to manage a single sound event including things + * like looping, volume and pitch changes. + */ + +#ifndef _SG_SOUND_HXX +#define _SG_SOUND_HXX 1 + +#ifndef __cplusplus +# error This library requires C++ +#endif + +#include +#include + +#include "sample_openal.hxx" +#include "soundmgr_openal.hxx" + +static const double MAX_TRANSIT_TIME = 0.1; // 100 ms. + + +/** + * Class for handling one sound event. + * + * This class handles everything for a particular sound event, by + * scanning an a pre-loaded property tree structure for sound + * settings, setting up its internal states, and managing sound + * playback whenever such an event happens. + */ +class SGXmlSound +{ + +public: + + SGXmlSound(); + virtual ~SGXmlSound(); + + /** + * Initialize the sound event. + * + * Prior to initialization of the sound event the propgrams property root + * has to be defined, the sound configuration XML tree has to be loaded + * and a sound manager class has to be defined. + * + * A sound configuration file would look like this: + * + * + * Define the name of the event. For refference only. + * Either: + * looped: play this sound looped. + * in-transit: play looped while the event is happening. + * once: play this sound once. + * The relative path to the audio file. + * Take action if this property becomes true. + * Take action if this condition becomes true. + * or Define volume or pitch settings. + * Take the value of this property as a refference for the + * result. + * Either: + * dt_start: the time elapsed since this sound is playing. + * dt_stop: the time elapsed since this sound has stopped. + * Add this value to the result. + * Multiply the result by this factor. + * Make sure the value is never less than this value. + * Make sure the value is never larger than this value. + * or + * + * + * + * + * + * + * @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 path The path where the audio files remain. + */ + virtual void init (SGPropertyNode *, SGPropertyNode *, SGSoundMgr *, + const string &); + + /** + * Check wheter an event has happened and if action has to be taken. + */ + virtual void update (double dt); + + /** + * Stop taking action on the pre-defined events. + */ + void stop(); + +protected: + + enum { MAXPROP=5 }; + enum { ONCE=0, LOOPED, IN_TRANSIT }; + enum { LEVEL=0, INVERTED, FLIPFLOP }; + + // SGXmlSound properties + typedef struct { + SGPropertyNode * prop; + double (*fn)(double); + double *intern; + double factor; + double offset; + double min; + double max; + bool subtract; + } _snd_prop; + +private: + + SGSoundMgr * _mgr; + SGSoundSample * _sample; + + SGCondition * _condition; + SGPropertyNode * _property; + + bool _active; + string _name; + int _mode; + double _prev_value; + double _dt_play; + double _dt_stop; + double _stopping; // time after the sound should have stopped. + // This is usefull for lost packets in in-trasit mode. + + vector<_snd_prop> _volume; + vector<_snd_prop> _pitch; + +}; + +#endif // _SG_SOUND_HXX