X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FSound%2Ffg_sound.cxx;h=5af54d59f78d1ffa1160f4b6288c4a88316cf75f;hb=0cbe8a597d877594623417bd30b1d5c4e131b023;hp=6fe6d37a76ed7f6ec2e5e046cbbceb4e341cc789;hpb=c5f43ca68835d6e35fafc75b9c32e3d78b9b5533;p=flightgear.git diff --git a/src/Sound/fg_sound.cxx b/src/Sound/fg_sound.cxx index 6fe6d37a7..5af54d59f 100644 --- a/src/Sound/fg_sound.cxx +++ b/src/Sound/fg_sound.cxx @@ -1,4 +1,4 @@ -// fg_sound.hxx -- Sound class implementation +// fg_sound.cxx -- Sound class implementation // // Started by Erik Hofman, February 2002 // (Reuses some code from fg_fx.cxx created by David Megginson) @@ -28,28 +28,27 @@ #else # include #endif -#include STL_STRING +#include #include +#include #include
#include "fg_sound.hxx" -SG_USING_STD(string); - -// static double _fg_lin(double v) { return v; }; -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_sqr(double v) { return pow(v, 2); }; -// static double _fg_pow3(double v) { return pow(v, 3); }; +// static double _fg_lin(double v) { return v; } +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); } +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); } static const struct { - string name; + char *name; double (*fn)(double); } __fg_snd_fn[] = { // {"lin", _fg_lin}, @@ -63,124 +62,114 @@ static const struct { {"", NULL} }; -FGSound::FGSound(const SGPropertyNode * node) - : _node(node), - _sample(NULL), +FGSound::FGSound() + : _sample(NULL), _active(false), - _mode(FGSound::ONCE), - _type(FGSound::LEVEL), - _name(""), - _factor(1.0) + _condition(NULL), + _property(NULL), + _dt_play(0.0), + _dt_stop(0.0), + _prev_value(0), + _name(""), + _mode(FGSound::ONCE) { } FGSound::~FGSound() { + if (_property) + delete _property; + + if (_condition) + delete _condition; + delete _sample; } void -FGSound::init() +FGSound::init(SGPropertyNode *node) { - _property = fgGetNode(_node->getStringValue("property"), true); - // - // seet sound global properties + // set global sound properties // - _name = _node->getStringValue("name"); - - if ((_factor = _node->getFloatValue("factor")) == 0.0) - _factor = 1.0; + + _name = node->getStringValue("name", ""); + SG_LOG(SG_GENERAL, SG_INFO, "Loading sound information for: " << _name ); - if ((_offset = _node->getFloatValue("offset")) == 0.0) - _offset = 0.0; + const char *mode_str = node->getStringValue("mode", ""); + if ( !strcmp(mode_str, "looped") ) { + _mode = FGSound::LOOPED; - SG_LOG(SG_GENERAL, SG_INFO, - "Loading sound information for: " << _name ); + } else if ( !strcmp(mode_str, "in-transit") ) { + _mode = FGSound::IN_TRANSIT; - if (_node->getStringValue("mode") == "looped") { - _mode = FGSound::LOOPED; } else { _mode = FGSound::ONCE; - if (_node->getStringValue("mode") != (string)"once") - SG_LOG( SG_GENERAL, SG_INFO, "Unknown sound mode, default to 'once'"); - } - - if (_node->getStringValue("type") == "flipflop") { - _type = FGSound::FLIPFLOP; - } else if (_node->getStringValue("type") == "inverted") { - _type = FGSound::INVERTED; - - } else if (_node->getStringValue("type") == "raise") { - _type = FGSound::RAISE; - - } else if (_node->getStringValue("type") == "fall") { - _type = FGSound::FALL; - - } else { - _type = FGSound::LEVEL; - if (_node->getStringValue("type") != (string)"level") - SG_LOG( SG_GENERAL, SG_INFO, "Unknown sound type, default to 'level'"); + if ( strcmp(mode_str, "") ) + SG_LOG(SG_GENERAL,SG_INFO, " Unknown sound mode, default to 'once'"); } + _property = fgGetNode(node->getStringValue("property", ""), true); + SGPropertyNode *condition = node->getChild("condition"); + if (condition != NULL) + _condition = fgReadCondition(condition); -#if 0 - // - // set position properties - // - _pos.dist = _node->getFloatValue("dist"); - _pos.hor = _node->getFloatValue("pos_hor"); - _pos.vert = _node->getFloatValue("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; + _snd_prop volume = {NULL, NULL, NULL, 1.0, 0.0, 0.0, 0.0, false}; - if ((volume.prop=fgGetNode(kids[i]->getStringValue("property"), true)) - == 0) - volume.prop = fgGetNode("/null", true); + if (strcmp(kids[i]->getStringValue("property"), "")) + volume.prop = fgGetNode(kids[i]->getStringValue("property", ""), true); - if ((volume.factor = kids[i]->getFloatValue("factor")) == 0.0) - volume.factor = 1.0; - else + 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; + else if (!strcmp(intern_str, "random")) + volume.intern = &_random; + + if ((volume.factor = kids[i]->getDoubleValue("factor", 1.0)) != 0.0) if (volume.factor < 0.0) { volume.factor = -volume.factor; volume.subtract = true; - } else - volume.subtract = false; - - volume.fn = NULL; - for (int j=0; __fg_snd_fn[j].fn; j++) - if (__fg_snd_fn[j].name == kids[i]->getStringValue("type")) { - volume.fn = __fg_snd_fn[j].fn; - break; - } + } + + const char *type_str = kids[i]->getStringValue("type", ""); + if ( strcmp(type_str, "") ) { - if (!volume.fn) - SG_LOG( SG_GENERAL, SG_INFO, "Unknown volume type, default to 'lin'"); + for (int j=0; __fg_snd_fn[j].fn; j++) + if ( !strcmp(type_str, __fg_snd_fn[j].name) ) { + volume.fn = __fg_snd_fn[j].fn; + break; + } - if ((volume.offset = kids[i]->getFloatValue("offset")) == 0.0) - volume.offset = 0.0; + if (!volume.fn) + SG_LOG(SG_GENERAL,SG_INFO, + " Unknown volume type, default to 'lin'"); + } - if ((volume.min = kids[i]->getFloatValue("min")) < 0.0) { + 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.min = 0.0; - } - if ((volume.max = kids[i]->getFloatValue("max")) <= volume.min) { - SG_LOG( SG_GENERAL, SG_ALERT, - "Volume maximum value below minimum value. Forced above minimum."); - volume.max = volume.min + 5.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; @@ -192,41 +181,49 @@ 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; + _snd_prop pitch = {NULL, NULL, NULL, 1.0, 1.0, 0.0, 0.0, false}; - if ((pitch.prop = fgGetNode(kids[i]->getStringValue("property"), true)) - == 0) - pitch.prop = fgGetNode("/null", true); + if (strcmp(kids[i]->getStringValue("property", ""), "")) + pitch.prop = fgGetNode(kids[i]->getStringValue("property", ""), true); - if ((pitch.factor = kids[i]->getFloatValue("factor")) == 0.0) - pitch.factor = 1.0; + 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; - pitch.fn = NULL; - for (int j=0; __fg_snd_fn[j].fn; j++) - if(__fg_snd_fn[j].name == kids[i]->getStringValue("type")) { - pitch.fn = __fg_snd_fn[j].fn; - break; - } + if ((pitch.factor = kids[i]->getDoubleValue("factor", 1.0)) != 0.0) + if (pitch.factor < 0.0) { + pitch.factor = -pitch.factor; + pitch.subtract = true; + } - if (!pitch.fn) - SG_LOG( SG_GENERAL, SG_INFO, "Unknown pitch type, default to 'lin'"); - - if ((pitch.offset = kids[i]->getFloatValue("offset")) == 0.0) - pitch.offset = 1.0; + const char *type_str = kids[i]->getStringValue("type", ""); + if ( strcmp(type_str, "") ) { - if ((pitch.min = kids[i]->getFloatValue("min")) < 0.0) { - SG_LOG( SG_GENERAL, SG_WARN, - "Pitch minimum value below 0. Forced to 0."); - pitch.min = 0.0; - } + for (int j=0; __fg_snd_fn[j].fn; j++) + if ( !strcmp(type_str, __fg_snd_fn[j].name) ) { + pitch.fn = __fg_snd_fn[j].fn; + break; + } - if ((pitch.max = kids[i]->getFloatValue("max")) <= pitch.min) { - SG_LOG( SG_GENERAL, SG_ALERT, - "Pitch maximum value below minimum value. Forced above minimum."); - pitch.max = pitch.min + 5.0; + 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; @@ -235,17 +232,12 @@ FGSound::init() // // Initialize the sample // - FGSoundMgr * mgr = globals->get_soundmgr(); - if (mgr->find(_name) == NULL) { - _sample = mgr->add(_name, _node->getStringValue("path")); - _sample->set_volume(v); - _sample->set_pitch(p); + _mgr = globals->get_soundmgr(); + if ((_sample = _mgr->find(_name)) == NULL) + _sample = _mgr->add(_name, node->getStringValue("path", "")); - } else { - _sample = mgr->find(_name); - _sample->set_volume(_sample->get_volume() + v); - _sample->set_pitch(_sample->get_pitch() + p); - } + _sample->set_volume(v); + _sample->set_pitch(p); } void @@ -259,85 +251,95 @@ FGSound::unbind () } void -FGSound::update (int dt) +FGSound::update (double 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 ( // Lisp, anyone? + (_condition && !_condition->test()) || + (!_condition && _property && + ( + !curr_value || + ( (_mode == FGSound::IN_TRANSIT) && (curr_value == _prev_value) ) + ) + ) + ) + { + + _active = false; + _dt_stop += dt; + _dt_play = 0.0; + + if (_sample->is_playing()) { + SG_LOG(SG_GENERAL, SG_INFO, "Stopping audio after " << _dt_play + << " sec: " << _name ); + _sample->stop( _mgr->get_scheduler() ); + } - // if (!_property) - // return; - - if ((_type == FGSound::LEVEL) || (_type == FGSound::INVERTED)) { + return; - bool check = (int)(_offset + _property->getFloatValue() * _factor); - if (_type == FGSound::INVERTED) - check = !check; + } - // - // If the state changes to false, stop playing. - // - if (!check) { - _active = false; - if (mgr->is_playing(_name)) { - SG_LOG(SG_GENERAL, SG_INFO, "Stopping sound: " << _name); - mgr->stop(_name); - } + // + // If the mode is ONCE and the sound is still playing, + // we have nothing to do anymore. + // + if (_active && (_mode == FGSound::ONCE)) { - return; - } + if (!_sample->is_playing()) { + _dt_stop += dt; + _dt_play = 0.0; - // - // 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->getFloatValue() * _factor); - if (check == _active) - return; - - // - // Check for state changes. - // If the state changed, and the sound is still playing: stop playing. - // - if (mgr->is_playing(_name)) { - SG_LOG(SG_GENERAL, SG_INFO, "Stopping sound: " << _name); - mgr->stop(_name); - } + } else + _dt_play += dt; - if ( ((_type == FGSound::RAISE) && !check) || - ((_type == FGSound::FALL) && check) ) - return; + return; } + // + // Update playtime, cache the current value and feed the random number + // + _dt_play += dt; + _prev_value = curr_value; + _random = sg_random(); + // // Update the volume // + int i; int max = _volume.size(); + double volume = 1.0; + double volume_offset = 0.0; - int i; - double volume = 1.0, volume_offset = 0.0; for(i = 0; i < max; i++) { - double v = _volume[i].prop->getDoubleValue(); + 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 (v > _volume[i].max) + if (_volume[i].max && (v > _volume[i].max)) v = _volume[i].max; - else - if (v < _volume[i].min) - v = 0; // v = _volume[i].min; + + 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; @@ -348,23 +350,32 @@ FGSound::update (int dt) // Update the pitch // max = _pitch.size(); - double pitch = 1.0, pitch_offset = 0.0; + double pitch = 1.0; + double pitch_offset = 0.0; + for(i = 0; i < max; i++) { - double p = _pitch[i].prop->getDoubleValue(); + 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 (p > _pitch[i].max) + if (_pitch[i].max && (p > _pitch[i].max)) p = _pitch[i].max; - else - if (p < _pitch[i].min) - p = _pitch[i].min; + + 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; @@ -377,26 +388,24 @@ FGSound::update (int dt) _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; - - if (_mode == FGSound::ONCE) - _sample->play(mgr->get_scheduler(), false); - 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_BULK, "Initial volume: " << volume_offset); - SG_LOG(SG_GENERAL, SG_BULK, "Initial pitch: " << pitch_offset); + if (_mode == FGSound::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; + } }