From f6b6cfc645175984d5b86cd24da56f52206ca49e Mon Sep 17 00:00:00 2001
From: Torsten Dreyer <torsten@t3r.de>
Date: Wed, 23 Apr 2014 17:09:00 +0200
Subject: [PATCH] voice system: all doors in FLITE

Prepare for festival lite generated voices
---
 src/Sound/voice.cxx | 154 ++++++++++++++++++++++++++++++--------------
 src/Sound/voice.hxx |  56 ++++++----------
 2 files changed, 123 insertions(+), 87 deletions(-)

diff --git a/src/Sound/voice.cxx b/src/Sound/voice.cxx
index 2143ba78f..04f8c4ef7 100644
--- a/src/Sound/voice.cxx
+++ b/src/Sound/voice.cxx
@@ -30,35 +30,51 @@
 #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/working", 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();
-	delete _thread;
-#endif
 }
 
 
@@ -67,14 +83,30 @@ 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");
-	try {
-		for (unsigned int i = 0; i < voices.size(); i++)
-			_voices.push_back(new FGVoice(this, voices[i]));
-	} catch (const std::string& s) {
-		SG_LOG(SG_SOUND, SG_ALERT, "VOICE: " << s);
-	}
+  for (unsigned int i = 0; i < voices.size(); i++) {
+    SGPropertyNode_ptr voice = voices[i];
+    if( voice->getBoolValue("festival", false ) ) {
+      try {
+        SG_LOG(SG_ALL,SG_ALERT,"creating festival voice" );
+        _voices.push_back(new FGFestivalVoice(this, voice));
+      } catch (const std::string& s) {
+        SG_LOG(SG_SOUND, SG_ALERT, "VOICE: " << s);
+      }
+    } else {
+#if defined(ENABLE_FLITE)
+      SG_LOG(SG_ALL,SG_ALERT,"creating flite voice" );
+      _voices.push_back(new FGFLITEVoice(this, voice));
+#else
+      SG_LOG(SG_ALL,SG_ALERT,"non festival voice not supported." );
+#endif
+    }
+  }
 
 #if defined(ENABLE_THREADS)
 	_thread->setProcessorAffinity(1);
@@ -82,6 +114,19 @@ void FGVoiceMgr::init()
 #endif
 }
 
+void FGVoiceMgr::shutdown()
+{
+#if defined(ENABLE_THREADS)
+  _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)
 {
@@ -95,7 +140,6 @@ void FGVoiceMgr::update(double)
 		_voices[i]->speak();
 #endif
 	}
-
 }
 
 
@@ -103,12 +147,11 @@ 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_SOUND, SG_INFO, "VOICE: adding `" << node->getStringValue("desc", "<unnamed>")
 			<< "' voice");
@@ -120,7 +163,7 @@ FGVoiceMgr::FGVoice::FGVoice(FGVoiceMgr *mgr, const SGPropertyNode_ptr node) :
 	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);
@@ -140,42 +183,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())
-		return false;
+  if (_msg.empty())
+    return false;
 
-	const string s = _msg.front();
-	_msg.pop();
+  const string s = _msg.front();
+  _msg.pop();
+
+  speak(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 (_festival) {
 		double d;
 		d = _volumeNode->getDoubleValue();
 		if (d != _volume)
@@ -186,11 +246,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 +258,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 +268,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,9 +296,9 @@ 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();
@@ -273,10 +329,8 @@ void FGVoiceMgr::FGVoice::FGVoiceListener::valueChanged(SGPropertyNode *node)
 			m += c;
 	}
 	//cerr << "\033[31;1mAFTER [" << m << "]\033[m" << endl;
-	if (_voice->_festival)
-		m = string("(SayText \"") + m + "\")";
 
-	_voice->pushMessage(m);
+	pushMessage(m);
 }
 
 
diff --git a/src/Sound/voice.hxx b/src/Sound/voice.hxx
index 4bde11d1e..eabd00a40 100644
--- a/src/Sound/voice.hxx
+++ b/src/Sound/voice.hxx
@@ -55,11 +55,13 @@ public:
 	FGVoiceMgr();
 	~FGVoiceMgr();
 	void init(void);
+	void shutdown();
 	void update(double dt);
 
-private:
-	class FGVoice;
 
+  class FGVoice;
+protected:
+  friend class FGFestivalVoice;
 #if defined(ENABLE_THREADS)
 	class FGVoiceThread;
 	FGVoiceThread *_thread;
@@ -86,53 +88,33 @@ private:
 	void wait_for_jobs() { OpenThreads::ScopedLock<OpenThreads::Mutex> g(_mutex); _jobs.wait(&_mutex); }
 	OpenThreads::Condition _jobs;
 	OpenThreads::Mutex _mutex;
+protected:
 	FGVoiceMgr *_mgr;
 };
 #endif
 
 
-
-class FGVoiceMgr::FGVoice {
+class FGVoiceMgr::FGVoice : public SGPropertyChangeListener {
 public:
-	FGVoice(FGVoiceMgr *, const SGPropertyNode_ptr);
-	~FGVoice();
-	bool speak();
-	void update();
-	void setVolume(double);
-	void setPitch(double);
-	void setSpeed(double);
-	void pushMessage(std::string);
+  FGVoice(FGVoiceMgr * mgr ) : _mgr(mgr) {}
+  virtual ~FGVoice() {}
+  virtual void speak( const std::string & msg ) = 0;
+  virtual void update() = 0;
+  void pushMessage( const std::string & m);
+  bool speak();
 
-private:
-	class FGVoiceListener;
-	SGSocket *_sock;
-	double _volume;
-	double _pitch;
-	double _speed;
-	SGPropertyNode_ptr _volumeNode;
-	SGPropertyNode_ptr _pitchNode;
-	SGPropertyNode_ptr _speedNode;
-	bool _festival;
-	FGVoiceMgr *_mgr;
+protected:
+  void valueChanged(SGPropertyNode *node);
 
-#if defined(ENABLE_THREADS)
-	SGLockedQueue<std::string> _msg;
+  FGVoiceMgr *_mgr;
+
+  #if defined(ENABLE_THREADS)
+  SGLockedQueue<std::string> _msg;
 #else
   std::queue<std::string> _msg;
 #endif
 
+
 };
 
-
-
-class FGVoiceMgr::FGVoice::FGVoiceListener : public SGPropertyChangeListener {
-public:
-	FGVoiceListener(FGVoice *voice) : _voice(voice) {}
-	void valueChanged(SGPropertyNode *node);
-
-private:
-	FGVoice *_voice;
-};
-
-
 #endif // _VOICE_HXX