From e8cb3cbfa4082db99cba8cf1bb6327b6e0eef24e Mon Sep 17 00:00:00 2001 From: ehofman Date: Fri, 9 May 2003 17:29:06 +0000 Subject: [PATCH] Move the FlightGear sound code over to SimGear --- configure.ac | 1 + simgear/Makefile.am | 1 + simgear/compatibility/Makefile.am | 1 + simgear/compatibility/new | 12 + simgear/sound/.cvsignore | 3 + simgear/sound/Makefile.am | 11 + simgear/sound/sound.cxx | 408 +++++++++++++++++++++++++++++ simgear/sound/sound.hxx | 96 +++++++ simgear/sound/soundmgr.cxx | 416 ++++++++++++++++++++++++++++++ simgear/sound/soundmgr.hxx | 201 +++++++++++++++ 10 files changed, 1150 insertions(+) create mode 100644 simgear/compatibility/new create mode 100644 simgear/sound/.cvsignore create mode 100644 simgear/sound/Makefile.am create mode 100644 simgear/sound/sound.cxx create mode 100644 simgear/sound/sound.hxx create mode 100644 simgear/sound/soundmgr.cxx create mode 100644 simgear/sound/soundmgr.hxx diff --git a/configure.ac b/configure.ac index eeab41a4..4c2f1a80 100644 --- a/configure.ac +++ b/configure.ac @@ -423,6 +423,7 @@ AC_CONFIG_FILES([ \ simgear/serial/Makefile \ simgear/sky/Makefile \ simgear/sky/clouds3d/Makefile \ + simgear/sound/Makefile \ simgear/threads/Makefile \ simgear/timing/Makefile \ simgear/xgl/Makefile \ diff --git a/simgear/Makefile.am b/simgear/Makefile.am index ccdcf2bf..77b86ed3 100644 --- a/simgear/Makefile.am +++ b/simgear/Makefile.am @@ -29,6 +29,7 @@ SUBDIRS = \ screen \ serial \ sky \ + sound \ $(SGTHREAD_DIR) \ timing \ xgl diff --git a/simgear/compatibility/Makefile.am b/simgear/compatibility/Makefile.am index 2c0592fe..25427d3e 100644 --- a/simgear/compatibility/Makefile.am +++ b/simgear/compatibility/Makefile.am @@ -27,4 +27,5 @@ include_HEADERS = \ cstddef \ ctime \ iomanip \ + new \ streambuf diff --git a/simgear/compatibility/new b/simgear/compatibility/new new file mode 100644 index 00000000..12f95be8 --- /dev/null +++ b/simgear/compatibility/new @@ -0,0 +1,12 @@ + +#ifndef __SG_NEW +#define __SG_NEW 1 + +#if defined(sgi) && !defined(__GNUC__) + +# include + +#endif + +#endif // !__SG_NEW + diff --git a/simgear/sound/.cvsignore b/simgear/sound/.cvsignore new file mode 100644 index 00000000..e9955884 --- /dev/null +++ b/simgear/sound/.cvsignore @@ -0,0 +1,3 @@ +.deps +Makefile +Makefile.in diff --git a/simgear/sound/Makefile.am b/simgear/sound/Makefile.am new file mode 100644 index 00000000..4aa9f322 --- /dev/null +++ b/simgear/sound/Makefile.am @@ -0,0 +1,11 @@ +includedir = @includedir@/sound + +lib_LIBRARIES = libsgsound.a + +noinst_HEADERS = + +include_HEADERS = sound.hxx soundmgr.hxx + +libsgsound_a_SOURCES = sound.cxx soundmgr.cxx + +INCLUDES = -I$(top_srcdir) diff --git a/simgear/sound/sound.cxx b/simgear/sound/sound.cxx new file mode 100644 index 00000000..c428d03d --- /dev/null +++ b/simgear/sound/sound.cxx @@ -0,0 +1,408 @@ +// 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 "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 : log10(v); } +static double _snd_log(double v) { return (v < 1) ? 0 : log(v); } +// static double _snd_sqr(double v) { return pow(v, 2); } +// static double _snd_pow3(double v) { return pow(v, 3); } + +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} +}; + +Sound::Sound() + : _sample(NULL), + _condition(NULL), + _property(NULL), + _active(false), + _name(""), + _mode(Sound::ONCE), + _prev_value(0), + _dt_play(0.0), + _dt_stop(0.0), + _stopping(0.0) +{ +} + +Sound::~Sound() +{ + _mgr->get_scheduler()->stopSample(_sample->get_sample()); + + if (_property) + delete _property; + + if (_condition) + delete _condition; + + _volume.clear(); + _pitch.clear(); + delete _sample; +} + +void +Sound::init(SGPropertyNode *root, SGPropertyNode *node, SoundMgr *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 = Sound::LOOPED; + + } else if ( !strcmp(mode_str, "in-transit") ) { + _mode = Sound::IN_TRANSIT; + + } else { + _mode = Sound::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 = fgReadCondition(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 < Sound::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 < Sound::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 +Sound::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 == Sound::IN_TRANSIT) && (curr_value == _prev_value) ) + ) + ) + ) + { + if ((_mode != Sound::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 == Sound::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 == Sound::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 new file mode 100644 index 00000000..cd0c96ed --- /dev/null +++ b/simgear/sound/sound.hxx @@ -0,0 +1,96 @@ +// 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$ + +#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. + * + */ +class Sound +{ + +public: + + Sound(); + virtual ~Sound(); + + virtual void init (SGPropertyNode *, SGPropertyNode *, SoundMgr *, + const string &); + virtual void update (double dt); + + void stop(); + +protected: + + enum { MAXPROP=5 }; + enum { ONCE=0, LOOPED, IN_TRANSIT }; + enum { LEVEL=0, INVERTED, FLIPFLOP }; + + // Sound properties + typedef struct { + SGPropertyNode * prop; + double (*fn)(double); + double *intern; + double factor; + double offset; + double min; + double max; + bool subtract; + } _snd_prop; + +private: + + SoundMgr * _mgr; + SimpleSound * _sample; + + FGCondition * _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 new file mode 100644 index 00000000..e5c53507 --- /dev/null +++ b/simgear/sound/soundmgr.cxx @@ -0,0 +1,416 @@ +// 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 ) + +// +// SimpleSound +// + +// constructor +SimpleSound::SimpleSound( 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 ); +} + +SimpleSound::SimpleSound( 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 +SimpleSound::~SimpleSound() { + delete pitch_envelope; + delete volume_envelope; + delete sample; +} + +void SimpleSound::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 SimpleSound::stop( slScheduler *sched, bool quick ) { + + sched->stopSample( sample ); +} + +// +// Sound Manager +// + +// constructor +SoundMgr::SoundMgr() { + audio_sched = new slScheduler( 8000 ); + if ( audio_sched->notWorking() ) { + SG_LOG( SG_GENERAL, SG_ALERT, "Audio initialization failed!" ); + } else { + audio_sched -> setMaxConcurrent ( 6 ); + + audio_mixer = new smMixer; + + SG_LOG( SG_GENERAL, SG_INFO, + "Rate = " << audio_sched->getRate() + << " Bps = " << audio_sched->getBps() + << " Stereo = " << audio_sched->getStereo() ); + } +} + +// destructor + +SoundMgr::~SoundMgr() { + + // + // 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 ) { + SimpleSound *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 SoundMgr::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 ) { + SimpleSound *s = sound_current->second; + + audio_sched->stopSample(s->get_sample()); + delete s->get_sample(); + delete s; + } + sounds.clear(); + +} + + +void SoundMgr::bind () +{ + // no properties yet +} + + +void SoundMgr::unbind () +{ + // no properties yet +} + + +// run the audio scheduler +void SoundMgr::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 +SoundMgr::pause () +{ + audio_sched->pauseSample(0, 0); +} + + +void +SoundMgr::resume () +{ + audio_sched->resumeSample(0, 0); +} + + +// add a sound effect, return true if successful +bool SoundMgr::add( SimpleSound *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 +SimpleSound *SoundMgr::add( const string &refname, + const char *path, const char *file ) { + SimpleSound *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 SimpleSound(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 SimpleSound(sr->sample->getBuffer(), sr->sample->getLength()); + sounds[refname] = sound; + + } + + return sound; +} + + +// remove a sound effect, return true if successful +bool SoundMgr::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) + SimpleSound *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 SoundMgr::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 SimpleSound if the specified sound exists +// in the sound manager system, otherwise return NULL +SimpleSound *SoundMgr::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 SoundMgr::play_looped( const string &refname ) { + SimpleSound *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 SoundMgr::play_once( const string& refname ) { + SimpleSound *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 SoundMgr::is_playing( const string& refname ) { + SimpleSound *sample; + + if ((sample = find( refname )) == NULL) + return false; + + return (sample->get_sample()->getPlayCount() > 0 ); +} + + +// immediate stop playing the sound +bool SoundMgr::stop( const string& refname ) { + SimpleSound *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 new file mode 100644 index 00000000..96245999 --- /dev/null +++ b/simgear/sound/soundmgr.hxx @@ -0,0 +1,201 @@ +// 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$ + + +#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 SimpleSound { + +private: + + slSample *sample; + slEnvelope *pitch_envelope; + slEnvelope *volume_envelope; + double pitch; + double volume; + +public: + + SimpleSound( const char *path, const char *file = NULL ); + SimpleSound( unsigned char *buffer, int len ); + ~SimpleSound(); + + void play( slScheduler *sched, bool looped ); + void stop( slScheduler *sched, bool quick = true ); + + inline void play_once( slScheduler *sched ) { play( sched, false); } + inline void play_looped( slScheduler *sched ) { play( sched, true); } + inline bool is_playing( ) { + return ( sample->getPlayCount() > 0 ); + } + + inline double get_pitch() const { return pitch; } + inline void set_pitch( double p ) { + pitch = p; + pitch_envelope->setStep( 0, 0.01, pitch ); + } + inline double get_volume() const { return volume; } + inline void set_volume( double v ) { + volume = v; + volume_envelope->setStep( 0, 0.01, volume ); + } + + inline slSample *get_sample() { return sample; } + inline slEnvelope *get_pitch_envelope() { return pitch_envelope; } + 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, SimpleSound * > sound_map; +typedef sound_map::iterator sound_map_iterator; +typedef sound_map::const_iterator const_sound_map_iterator; + + +class SoundMgr +{ + + slScheduler *audio_sched; + smMixer *audio_mixer; + + sound_map sounds; + sample_map samples; + + double safety; + +public: + + SoundMgr(); + ~SoundMgr(); + + + /** + * (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( SimpleSound *sound, const string& refname); + + // add a sound file, return the sample if successful, else return NULL + SimpleSound *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 SimpleSound if the specified sound + // exists in the sound manager system, otherwise return NULL + SimpleSound *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 + + -- 2.39.5