]> git.mxchange.org Git - flightgear.git/blobdiff - src/Sound/fg_sound.cxx
Patch from Julian Foad:
[flightgear.git] / src / Sound / fg_sound.cxx
index aa22b6fed01cfc8be933735aaadffe60c40e17c8..5af54d59f78d1ffa1160f4b6288c4a88316cf75f 100644 (file)
@@ -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)
 #else
 #  include <math.h>
 #endif
-#include STL_STRING
+#include <string.h>
 
 #include <simgear/debug/logstream.hxx>
+#include <simgear/math/sg_random.h>
 
 #include <Main/fg_props.hxx>
 
 #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<const SGPropertyNode *> kids = _node->getChildren("volume");
+   vector<SGPropertyNode_ptr> 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;
 
-      int check = (_offset + _property->getFloatValue() * _factor);
-      if (_type == FGSound::INVERTED)
-         check = !check;
+   }
 
-      //
-      // If the state changes to false, stop playing.
-      //
-      if (check == 0) {
-         _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
-
-      int check = (_offset + _property->getFloatValue() * _factor);
-      if ((bool)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)
-      mgr->play_once(_name);
-   else
-      mgr->play_looped(_name);
-
-   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;
+   }
 }