diff --git a/docs-mini/README.xmlsound b/docs-mini/README.xmlsound index b63899951..4b9632340 100644 --- a/docs-mini/README.xmlsound +++ b/docs-mini/README.xmlsound @@ -1,5 +1,5 @@ Users Guide to FlightGear sound configuration -Version 0.7.10, Mar 02 2002 +Version 0.7.11, apr 27 2002 Author: Erik Hofman This document is an attempt to describe the configuration of @@ -44,7 +44,9 @@ A limited sound configuration file would look something like this: engine Sounds/wasp.wav looped - /engines/engine/running + + /engines/engine/running + /engines/engine/mp-osi 0.005 @@ -81,7 +83,7 @@ Configuration description: < ... > This is the event seperator. The text inside the brackets can be anything. Bit it is adviced to give it a meaningfull name - like: crank, engine, rumble, gear, squeal, flap, wind or stall + like: crank, engine, rumble, gear, squeal, flap, wind or stall The value can be defined multiple times, thus anything which is related may have the same name (grouping them together). @@ -89,7 +91,7 @@ Configuration description: This defines the name of the event. This name is used internally and, although it can me defined multiple times in the same file, - should normally have an unique value. + should normally have an unique value. Multiple definitions of the same name will allow multiple sections to interfere in the starting and stopping of the sample. @@ -104,7 +106,7 @@ Configuration description: Using the type "fall" will stop playback when the event turns true. IMPORTANT: - If the trigger is used for anything else but stopping the sound + If the trigger is used for anything else but stopping the sound at a certain event, all sections with the same name *should* have exactly the same sections for everything but property and type. @@ -114,43 +116,21 @@ Configuration description: This defined th path to the sound file. The path is relative to the FlightGear root directory but could be specified absolute. + + + Define a condition that triggers the event. + For a complete description of the FlightGear conditions, + please read docs-mini/README.conditions + + An event should define either a condition or a property. Define which property triggers the event, and reffers to a node - in the FlightGear property tree. - - The value is converted to an integer value (anything less than 0.5 is - is considered to be 0) and handled if it were a boolean value - (0 = false, anything else = true). - - The triger depends on the value of . - - - This specifies how the event is triggered. When an event is triggered - the sample will start playing. Since the effects scheduler can have - multiple events controll a single sound event, it depends on the - situation if an event actually stops playing the sound. + in the FlightGear property tree. Action is taken when the property + is non zero. - Basically the following is true: - The first event requesting to start playback, triggers playback. - The last event requesting to stop playback, will stop playback. - - There are multiple options: - - level: events are active if the value is true. - this is the default behaviour. - - inverted: events are active if the value is false. - - flipflop: events are triggered on state changes. - this is only usefull for samples which are played - once. - - raise: start playing at the raise of the event. - explicitly stop playing when the event turns false. - - fall: start playing at the fall of the event. - explicitly stop playing when the event turns true. + A more sophisticated mechanism to trigger the event is described + in This defines how the sample should be played: @@ -160,7 +140,9 @@ Configuration description: looped: the sample plays continuesly, until the event turns false. - + + in-transit: the sample plays continuesly, + while the property is changing its value. / Volume or Pitch definition. Currently there may be up to 5 @@ -183,10 +165,12 @@ Configuration description: this is the default. ln: convert the property value to a natural logarithmic - value before scaling it. + value before scaling it. Anything below 1 will return + zero. log: convert the property value to a true logarithmic - value before scaling it. + value before scaling it. Anything below 1 will return + zero. inv: inverse lineair handling (1/x). @@ -211,7 +195,6 @@ Configuration description: will be truncated to this value. - Maximum allowed value. This is usefull if sounds gets to loud. Anything higher will be truncated to this value. @@ -227,7 +210,7 @@ Default values are: type: lin factor: 1.0 offset: 0.0 for volume, 1.0 for pitch -min: 0.0 (don't check) +min: 0.0 max: 0.0 (don't check) @@ -240,12 +223,12 @@ Calculations are made the following way (for both pitch and volume): for (n = 0; n < max; n++) { if (factor < 0) { - value += offset[n] - abs(factor[n]) * function(property[n]); + value += offset[n] - abs(factor[n]) * function(property[n]); } else { - value += factor[n] * function(property[n]); - offs += offset[n]; + value += factor[n] * function(property[n]); + offs += offset[n]; } } diff --git a/src/Sound/fg_fx.cxx b/src/Sound/fg_fx.cxx index 2636bc011..0c31746c0 100644 --- a/src/Sound/fg_fx.cxx +++ b/src/Sound/fg_fx.cxx @@ -52,7 +52,7 @@ FGFX::~FGFX () void FGFX::init() { - const SGPropertyNode * node = fgGetNode("/sim/sound", true); + SGPropertyNode * node = fgGetNode("/sim/sound", true); int i; string path_str = node->getStringValue("path"); @@ -77,13 +77,10 @@ FGFX::init() node = root.getNode("fx"); for (i = 0; i < node->nChildren(); i++) { - FGSound * sound; - sound = new FGSound(node->getChild(i)); - _sound.push_back(sound); - } + FGSound *sound = new FGSound(); + sound->init(node->getChild(i)); - for (i = 0; i < (int)_sound.size(); i++ ) { - _sound[i]->init(); + _sound.push_back(sound); } } diff --git a/src/Sound/fg_sound.cxx b/src/Sound/fg_sound.cxx index 34d8f4874..3701766c1 100644 --- a/src/Sound/fg_sound.cxx +++ b/src/Sound/fg_sound.cxx @@ -41,8 +41,8 @@ static double _fg_inv(double v) { return (v == 0) ? 1e99 : 1/v; }; static double _fg_abs(double v) { return (v >= 0) ? v : -v; }; static double _fg_sqrt(double v) { return (v < 0) ? sqrt(-v) : sqrt(v); }; -static double _fg_log10(double v) { return (v < 1) ? 0 : log10(v+1); }; -static double _fg_log(double v) { return (v < 1) ? 0 : log(v+1); }; +static double _fg_log10(double v) { return (v < 1) ? 0 : log10(v); }; +static double _fg_log(double v) { return (v < 1) ? 0 : log(v); }; // static double _fg_sqr(double v) { return pow(v, 2); }; // static double _fg_pow3(double v) { return pow(v, 3); }; @@ -61,44 +61,40 @@ static const struct { {"", NULL} }; -FGSound::FGSound(const SGPropertyNode * node) - : _node(node), +FGSound::FGSound() + : _condition(NULL), + _property(NULL), _sample(NULL), - _active(false), _mode(FGSound::ONCE), - _type(FGSound::LEVEL), - _name(""), - _factor(1.0) + _prev_value(0), + _name("") { } FGSound::~FGSound() { + delete _condition; delete _sample; } void -FGSound::init() +FGSound::init(SGPropertyNode *node) { - _property = fgGetNode(_node->getStringValue("property"), true); - // // set global sound properties // - _name = _node->getStringValue("name"); - - if ((_factor = _node->getDoubleValue("factor")) == 0.0) - _factor = 1.0; - - _offset = _node->getDoubleValue("offset"); - + + _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") ) { + const char *mode_str = node->getStringValue("mode"); + if ( !strcmp(mode_str, "looped") ) { _mode = FGSound::LOOPED; + } else if ( !strcmp(mode_str, "in-transit") ) { + _mode = FGSound::IN_TRANSIT; + } else { _mode = FGSound::ONCE; @@ -106,41 +102,21 @@ FGSound::init() SG_LOG(SG_GENERAL,SG_INFO, " Unknown sound mode, default to 'once'"); } - const char *type_str = _node->getStringValue("type"); - if ( !strcmp(type_str, "flipflop") ) { - _type = FGSound::FLIPFLOP; + _property = fgGetNode(node->getStringValue("property"), true); + SGPropertyNode *condition = node->getChild("condition"); + if (condition != NULL) + _condition = fgReadCondition(condition); - } else if ( !strcmp(type_str, "inverted") ) { - _type = FGSound::INVERTED; - - } else if ( !strcmp(type_str, "raise") ) { - _type = FGSound::RAISE; - - } else if ( !strcmp(type_str, "fall") ) { - _type = FGSound::FALL; - - } else { - _type = FGSound::LEVEL; - - if ( strcmp(type_str, "") ) - SG_LOG(SG_GENERAL,SG_INFO, " Unknown sound type, default to 'level'"); - } - -#if 0 - // - // set position properties - // - _pos.dist = _node->getDoubleValue("dist"); - _pos.hor = _node->getDoubleValue("pos_hor"); - _pos.vert = _node->getDoubleValue("pos_vert"); -#endif + 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"); + vector kids = node->getChildren("volume"); for (i = 0; (i < kids.size()) && (i < FGSound::MAXPROP); i++) { _snd_prop volume; @@ -199,7 +175,7 @@ FGSound::init() // set pitch properties // float p = 0.0; - kids = _node->getChildren("pitch"); + kids = node->getChildren("pitch"); for (i = 0; (i < kids.size()) && (i < FGSound::MAXPROP); i++) { _snd_prop pitch; @@ -256,9 +232,9 @@ FGSound::init() // // Initialize the sample // - FGSoundMgr * mgr = globals->get_soundmgr(); - if ((_sample = mgr->find(_name)) == NULL) - _sample = mgr->add(_name, _node->getStringValue("path")); + _mgr = globals->get_soundmgr(); + if ((_sample = _mgr->find(_name)) == NULL) + _sample = _mgr->add(_name, node->getStringValue("path")); _sample->set_volume(v); _sample->set_pitch(p); @@ -277,150 +253,129 @@ FGSound::unbind () void FGSound::update (int dt) { - FGSoundMgr * mgr = globals->get_soundmgr(); + double curr_value = 0.0; // - // Do we have something to do? + // If the state changes to false, stop playing. // + if (_property) + curr_value = _property->getDoubleValue(); - // if (!_property) - // return; + if ( // Lisp, anyone? + (_condition && !_condition->test()) || + (_property && + ( + !curr_value || + ( (_mode == FGSound::IN_TRANSIT) && (curr_value == _prev_value) ) + ) + ) + ) + { - if ((_type == FGSound::LEVEL) || (_type == FGSound::INVERTED)) { - - // - // use an integer to get false when: -1 < check < 1 - // - bool check = (int)(_offset + _property->getDoubleValue() * _factor); - if (_type == FGSound::INVERTED) - check = !check; - - // - // If the state changes to false, stop playing. - // - if (!check) { - if (_active) { - SG_LOG(SG_GENERAL, SG_INFO, "Stopping sound: " << _name); - _sample->stop( mgr->get_scheduler(), false ); - _active = false; - } - - return; - } - - // - // If the sound is already playing we have nothing to do. - // - if (_active && (_mode == FGSound::ONCE)) - return; - - } else { // FLIPFLOP, RAISE, FALL - - bool check = (int)(_offset + _property->getDoubleValue() * _factor); - if (check == _active) - return; - - // - // Check for state changes. - // If the state changed, and the sound is still playing: stop playing. - // + _active = false; if (_sample->is_playing()) { SG_LOG(SG_GENERAL, SG_INFO, "Stopping sound: " << _name); - _sample->stop( mgr->get_scheduler() ); + _sample->stop( _mgr->get_scheduler() ); } - if ( ((_type == FGSound::RAISE) && !check) || - ((_type == FGSound::FALL) && check) ) - return; + return; } - { - int i, max; + // + // If the mode is ONCE and the sound is still playing, + // we have nothing to do anymore. + // + if (_active && (_mode == FGSound::ONCE)) + return; - // - // Update the volume - // - max = _volume.size(); - double volume = 1.0; - double volume_offset = 0.0; + // + // Cache current value; + // + _prev_value = curr_value; - for(i = 0; i < max; i++) { - double v = _volume[i].prop->getDoubleValue(); + // + // Update the volume + // + int i; + int max = _volume.size(); + double volume = 1.0; + double volume_offset = 0.0; - if (_volume[i].fn) - v = _volume[i].fn(v); + for(i = 0; i < max; i++) { + double v = _volume[i].prop->getDoubleValue(); - v *= _volume[i].factor; + if (_volume[i].fn) + v = _volume[i].fn(v); - if (!_volume[i].max && (v > _volume[i].max)) - v = _volume[i].max; + v *= _volume[i].factor; - else if (!_volume[i].min && (v < _volume[i].min)) - v = _volume[i].min; + if (_volume[i].max && (v > _volume[i].max)) + v = _volume[i].max; - if (_volume[i].subtract) // Hack! - volume = _volume[i].offset - v; - else { - volume_offset += _volume[i].offset; - volume *= v; - } + 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 = _pitch[i].prop->getDoubleValue(); - - 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 (!_pitch[i].min && (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 ); } + // + // Update the pitch + // + max = _pitch.size(); + double pitch = 1.0; + double pitch_offset = 0.0; + + for(i = 0; i < max; i++) { + double p = _pitch[i].prop->getDoubleValue(); + + 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 && ((_type == FGSound::LEVEL) || (_type == FGSound::INVERTED))) - return; + if (!_active) { - // - // This is needed for FGSound::FLIPFLOP and it works for - // FGSound::LEVEl. Doing it this way saves an extra 'if'. - // - _active = !_active; + _active = true; + if (_mode == FGSound::ONCE) + _sample->play(_mgr->get_scheduler(), false); - if (_mode == FGSound::ONCE) - _sample->play(mgr->get_scheduler(), false); - else - _sample->play(mgr->get_scheduler(), true); + else + _sample->play(_mgr->get_scheduler(), true); - SG_LOG(SG_GENERAL, SG_INFO, "Starting audio playback for: " << _name); - SG_LOG(SG_GENERAL, SG_BULK, - "Playing " << ((_mode == ONCE) ? "once" : "looped")); + SG_LOG(SG_GENERAL, SG_INFO, "Starting audio playback for: " << _name); + SG_LOG(SG_GENERAL, SG_BULK, + "Playing " << ((_mode == ONCE) ? "once" : "looped")); + } } diff --git a/src/Sound/fg_sound.hxx b/src/Sound/fg_sound.hxx index 37b2d2124..04dda074b 100644 --- a/src/Sound/fg_sound.hxx +++ b/src/Sound/fg_sound.hxx @@ -34,18 +34,18 @@ #include "soundmgr.hxx" /** - * Class for handling one sound. + * Class for handling one sound event. * */ -class FGSound : public FGSubsystem +class FGSound { public: - FGSound(const SGPropertyNode *); + FGSound(); virtual ~FGSound(); - virtual void init (); + virtual void init (SGPropertyNode *); virtual void bind (); virtual void unbind (); virtual void update (int dt); @@ -53,13 +53,13 @@ public: protected: enum { MAXPROP=5 }; - enum { ONCE=0, LOOPED }; - enum { LEVEL=0, INVERTED, FLIPFLOP, RAISE, FALL }; + enum { ONCE=0, LOOPED, IN_TRANSIT }; + enum { LEVEL=0, INVERTED, FLIPFLOP }; // Sound properties typedef struct { - const SGPropertyNode * prop; + SGPropertyNode * prop; double (*fn)(double); double factor; double offset; @@ -68,30 +68,18 @@ protected: bool subtract; } _snd_prop; -#if 0 - // Sound source (distance, horizontal position in degrees and - // vertical position in degrees) - typedef struct { - float dist; - float hor; - float vert; - } _pos_prop; -#endif - private: - const SGPropertyNode * _node; - + FGSoundMgr * _mgr; FGSimpleSound * _sample; - const SGPropertyNode * _property; + FGCondition * _condition; + + SGPropertyNode * _property; + double _prev_value; bool _active; - - int _mode; - int _type; string _name; - double _factor; - double _offset; + int _mode; vector<_snd_prop> _volume; vector<_snd_prop> _pitch; diff --git a/src/Sound/soundmgr.cxx b/src/Sound/soundmgr.cxx index 41947bb0f..fb805428d 100644 --- a/src/Sound/soundmgr.cxx +++ b/src/Sound/soundmgr.cxx @@ -41,8 +41,7 @@ // constructor FGSimpleSound::FGSimpleSound( string file ) : pitch(1.0), - volume(1.0), - requests(0) + volume(1.0) { SGPath slfile( globals->get_fg_root() ); slfile.append( file ); @@ -55,8 +54,7 @@ FGSimpleSound::FGSimpleSound( string file ) FGSimpleSound::FGSimpleSound( unsigned char *buffer, int len ) : pitch(1.0), - volume(1.0), - requests(0) + volume(1.0) { sample = new slSample ( buffer, len ); pitch_envelope = new slEnvelope( 1, SL_SAMPLE_ONE_SHOT ); @@ -74,14 +72,12 @@ FGSimpleSound::~FGSimpleSound() { void FGSimpleSound::play( slScheduler *sched, bool looped ) { - requests++; - // make sure sound isn't already playing if ( sample->getPlayCount() > 0 ) { - return; + sched->stopSample(sample); + // return; } - // sched->stopSample(sample); if ( looped ) { sched->loopSample(sample); } else { @@ -94,18 +90,6 @@ void FGSimpleSound::play( slScheduler *sched, bool looped ) { void FGSimpleSound::stop( slScheduler *sched, bool quick ) { - if ( quick ) { - requests = 0; - } else { - if ( --requests < 0 ) { - requests = 0; - } - } - - if ( requests > 0 ) { - return; - } - sched->stopSample( sample ); } diff --git a/src/Sound/soundmgr.hxx b/src/Sound/soundmgr.hxx index fbab39fc4..a46f31f53 100644 --- a/src/Sound/soundmgr.hxx +++ b/src/Sound/soundmgr.hxx @@ -57,7 +57,6 @@ private: slEnvelope *volume_envelope; double pitch; double volume; - int requests; public: