]> git.mxchange.org Git - flightgear.git/blobdiff - src/Sound/voice.cxx
Interim windows build fix
[flightgear.git] / src / Sound / voice.cxx
index ae23ae88b7738cad042b0525d21eea406d0d57a4..8817efe812ce511c278df9f1f3641317a63dd1b7 100644 (file)
 //
 // $Id$
 
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
 #include <Main/globals.hxx>
 #include <sstream>
 #include <simgear/compiler.h>
 #include <Main/fg_props.hxx>
 #include "voice.hxx"
 
+#if defined(ENABLE_FLITE)
+#include "flitevoice.hxx"
+#endif
+
 #define VOICE "/sim/sound/voices"
 
+using std::string;
+
+class FGFestivalVoice : public FGVoiceMgr::FGVoice {
+public:
+  FGFestivalVoice(FGVoiceMgr *, const SGPropertyNode_ptr);
+  virtual ~FGFestivalVoice();
+  virtual void speak( const string & msg );
+  virtual void update();
+  void setVolume(double);
+  void setPitch(double);
+  void setSpeed(double);
+
+private:
+  SGSocket *_sock;
+  double _volume;
+  double _pitch;
+  double _speed;
+  SGPropertyNode_ptr _volumeNode;
+  SGPropertyNode_ptr _pitchNode;
+  SGPropertyNode_ptr _speedNode;
+};
 
 /// MANAGER ///
 
 FGVoiceMgr::FGVoiceMgr() :
+#if defined(ENABLE_THREADS)
+  _thread(NULL),
+#endif
        _host(fgGetString(VOICE "/host", "localhost")),
        _port(fgGetString(VOICE "/port", "1314")),
        _enabled(fgGetBool(VOICE "/enabled", false)),
-       _pausedNode(fgGetNode("/sim/sound/pause", true))
+       _pausedNode(fgGetNode("/sim/sound/working", true)),
+       _paused(false)
 {
-#if defined(ENABLE_THREADS)
-       if (!_enabled)
-               return;
-       _thread = new FGVoiceThread(this);
-#endif
 }
 
 
 FGVoiceMgr::~FGVoiceMgr()
 {
-#if defined(ENABLE_THREADS)
-       if (!_enabled)
-               return;
-       _thread->cancel();
-       _thread->join();
-#endif
 }
 
 
@@ -61,30 +83,64 @@ void FGVoiceMgr::init()
        if (!_enabled)
                return;
 
+#if defined(ENABLE_THREADS)
+  _thread = new FGVoiceThread(this);
+#endif
+
        SGPropertyNode *base = fgGetNode(VOICE, true);
        vector<SGPropertyNode_ptr> voices = base->getChildren("voice");
-       for (unsigned int i = 0; i < voices.size(); i++)
-               _voices.push_back(new FGVoice(this, voices[i]));
+  for (unsigned int i = 0; i < voices.size(); i++) {
+    SGPropertyNode_ptr voice = voices[i];
+    if( voice->getBoolValue("festival", false ) ) {
+      try {
+        _voices.push_back(new FGFestivalVoice(this, voice));
+        continue;
+      } catch (const std::string& ) {
+        SG_LOG(SG_SOUND, SG_WARN, "failed to create festival voice, falling back to flite voice" );
+      }
+    }
+#if defined(ENABLE_FLITE)
+    SG_LOG(SG_SOUND,SG_INFO,"creating flite voice" );
+    _voices.push_back(new FGFLITEVoice(this, voice));
+#else
+    SG_LOG(SG_SOUND,SG_ALERT,"non festival voice not supported." );
+#endif
+  }
 
 #if defined(ENABLE_THREADS)
-       _thread->start(1);
+       _thread->setProcessorAffinity(1);
+       _thread->start();
 #endif
 }
 
+void FGVoiceMgr::shutdown()
+{
+#if defined(ENABLE_THREADS)
+  if( _thread ) {
+    _thread->cancel();
+    _thread->join();
+    delete _thread;
+    _thread = NULL;
+  }
+#endif
+
+  for( std::vector<FGVoice*>::iterator it = _voices.begin(); it != _voices.end(); ++it )
+    delete *it;
+}
+
 
 void FGVoiceMgr::update(double)
 {
        if (!_enabled)
                return;
 
-       _paused = _pausedNode->getBoolValue();
+       _paused = !_pausedNode->getBoolValue();
        for (unsigned int i = 0; i < _voices.size(); i++) {
                _voices[i]->update();
 #if !defined(ENABLE_THREADS)
                _voices[i]->speak();
 #endif
        }
-
 }
 
 
@@ -92,41 +148,32 @@ void FGVoiceMgr::update(double)
 
 /// VOICE ///
 
-FGVoiceMgr::FGVoice::FGVoice(FGVoiceMgr *mgr, const SGPropertyNode_ptr node) :
+FGFestivalVoice::FGFestivalVoice(FGVoiceMgr *mgr, const SGPropertyNode_ptr node) :
+  FGVoice(mgr),
        _volumeNode(node->getNode("volume", true)),
        _pitchNode(node->getNode("pitch", true)),
-       _speedNode(node->getNode("speed", true)),
-       _festival(node->getBoolValue("festival", true)),
-       _mgr(mgr)
+       _speedNode(node->getNode("speed", true))
 {
-       SG_LOG(SG_IO, SG_INFO, "VOICE: adding `" << node->getStringValue("desc", "<unnamed>")
+       SG_LOG(SG_SOUND, SG_INFO, "VOICE: adding `" << node->getStringValue("desc", "<unnamed>")
                        << "' voice");
        const string &host = _mgr->_host;
        const string &port = _mgr->_port;
 
        _sock = new SGSocket(host, port, "tcp");
-       _sock->set_timeout(10000);
-       _connected = _sock->open(SG_IO_OUT);
-       if (!_connected) {
-               SG_LOG(SG_IO, SG_ALERT, "VOICE: no connection to `"
-                               << host << ':' << port << '\'');
-               return;
-       }
+       _sock->set_timeout(6000);
+       if (!_sock->open(SG_IO_OUT))
+               throw string("no connection to `") + host + ':' + port + '\'';
 
-       if (_festival) {
+       {
                _sock->writestring("(SayText \"\")\015\012");
                char buf[4];
                int len = _sock->read(buf, 3);
-               if (len != 3 || buf[0] != 'L' || buf[1] != 'P') {
-                       SG_LOG(SG_IO, SG_ALERT, "VOICE: unexpected or no response from `"
-                                       << host << ':' << port << "'. Either it's not " << endl
-                                       << "       Festival listening, or Festival couldn't open a "
-                                       "sound device.");
-                       _connected = false;
-                       return;
-               }
-
-               SG_LOG(SG_IO, SG_BULK, "VOICE: connection to Festival server on `"
+               if (len != 3 || buf[0] != 'L' || buf[1] != 'P')
+                       throw string("unexpected or no response from `") + host + ':' + port
+                                       + "'. Either it's not\n       Festival listening,"
+                                       " or Festival couldn't open a sound device.";
+
+               SG_LOG(SG_SOUND, SG_INFO, "VOICE: connection to Festival server on `"
                                << host << ':' << port << "' established");
 
                setVolume(_volume = _volumeNode->getDoubleValue());
@@ -137,45 +184,59 @@ FGVoiceMgr::FGVoice::FGVoice(FGVoiceMgr *mgr, const SGPropertyNode_ptr node) :
        string preamble = node->getStringValue("preamble", "");
        if (!preamble.empty())
                pushMessage(preamble);
-
-       node->getNode("text", true)->addChangeListener(new FGVoiceListener(this));
+  node->getNode("text", true)->addChangeListener(this);
 }
 
 
-FGVoiceMgr::FGVoice::~FGVoice()
+FGFestivalVoice::~FGFestivalVoice()
 {
        _sock->close();
        delete _sock;
 }
 
 
-void FGVoiceMgr::FGVoice::pushMessage(string m)
+void FGVoiceMgr::FGVoice::pushMessage( const string & m)
 {
-       _msg.push(m + "\015\012");
+       _msg.push(m);
 #if defined(ENABLE_THREADS)
        _mgr->_thread->wake_up();
 #endif
 }
 
-
 bool FGVoiceMgr::FGVoice::speak(void)
 {
-       if (_msg.empty()) {
-//             cerr << "<nothing to say>" << endl;
-               return false;
-       }
+  if (_msg.empty())
+    return false;
+
+  const string s = _msg.front();
+  _msg.pop();
+
+  speak(s);
 
-       const string s = _msg.front();
-       _msg.pop();
-//     cerr << "POP " << s;
+  return !_msg.empty();
+}
+
+void FGFestivalVoice::speak( const string & msg )
+{
+  if( msg.empty() )
+    return;
+
+  string s;
+
+  if( msg[0] == '(' ) {
+    s = msg;
+  } else {
+    s.append("(SayText \"");
+    s.append(msg).append("\")");
+  }
+
+  s.append("\015\012");
        _sock->writestring(s.c_str());
-       return !_msg.empty();
 }
 
 
-void FGVoiceMgr::FGVoice::update(void)
+void FGFestivalVoice::update(void)
 {
-       if (_connected && _festival) {
                double d;
                d = _volumeNode->getDoubleValue();
                if (d != _volume)
@@ -186,11 +247,10 @@ void FGVoiceMgr::FGVoice::update(void)
                d = _speedNode->getDoubleValue();
                if (d != _speed)
                        setSpeed(_speed = d);
-       }
 }
 
 
-void FGVoiceMgr::FGVoice::setVolume(double d)
+void FGFestivalVoice::setVolume(double d)
 {
        std::ostringstream s;
        s << "(set! default_after_synth_hooks (list (lambda (utt)"
@@ -199,7 +259,7 @@ void FGVoiceMgr::FGVoice::setVolume(double d)
 }
 
 
-void FGVoiceMgr::FGVoice::setPitch(double d)
+void FGFestivalVoice::setPitch(double d)
 {
        std::ostringstream s;
        s << "(set! int_lr_params '((target_f0_mean " << d <<
@@ -209,16 +269,13 @@ void FGVoiceMgr::FGVoice::setPitch(double d)
 }
 
 
-void FGVoiceMgr::FGVoice::setSpeed(double d)
+void FGFestivalVoice::setSpeed(double d)
 {
        std::ostringstream s;
        s << "(Parameter.set 'Duration_Stretch " << d << ')';
        pushMessage(s.str());
 }
 
-
-
-
 /// THREAD ///
 
 #if defined(ENABLE_THREADS)
@@ -240,32 +297,41 @@ void FGVoiceMgr::FGVoiceThread::run(void)
 
 /// LISTENER ///
 
-void FGVoiceMgr::FGVoice::FGVoiceListener::valueChanged(SGPropertyNode *node)
+void FGVoiceMgr::FGVoice::valueChanged(SGPropertyNode *node)
 {
-       if (_voice->_mgr->_paused)
+       if (_mgr->_paused)
                return;
 
        const string s = node->getStringValue();
-//     cerr << "PUSH " << s << endl;
+       //cerr << "\033[31;1mBEFORE [" << s << "]\033[m" << endl;
 
        string m;
        for (unsigned int i = 0; i < s.size(); i++) {
                char c = s[i];
-               if (!isprint(c))
+               if (c == '\n' || c == '\r' || c == '\t')
+                       m += ' ';
+               else if (!isprint(c))
                        continue;
-               else if (c == '"' || c == '\\')
-                       m += '\\' + c;
+               else if (c == '\\' || c == '"')
+                       m += '\\', m += c;
                else if (c == '|' || c == '_')
                        m += ' ';       // don't say "vertical bar" or "underscore"
                else if (c == '&')
                        m += " and ";
+        else if (c == '>' || c == '<')
+            m += ' ';      // don't say "greater than" or "less than" either
+               else if (c == '{') {
+                       while (i < s.size())
+                               if (s[++i] == '|')
+                                       break;
+               } else if (c == '}')
+                       ;
                else
                        m += c;
        }
-       if (_voice->_festival)
-               m = string("(SayText \"") + m + "\")";
+       //cerr << "\033[31;1mAFTER [" << m << "]\033[m" << endl;
 
-       _voice->pushMessage(m);
+       pushMessage(m);
 }