]> git.mxchange.org Git - flightgear.git/blob - src/Sound/voice.cxx
use flite+hts for metar
[flightgear.git] / src / Sound / voice.cxx
1 // speech synthesis interface subsystem
2 //
3 // Written by Melchior FRANZ, started February 2006.
4 //
5 // Copyright (C) 2006  Melchior FRANZ - mfranz@aon.at
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 //
21 // $Id$
22
23 #ifdef HAVE_CONFIG_H
24 #  include "config.h"
25 #endif
26
27 #include <Main/globals.hxx>
28 #include <sstream>
29 #include <simgear/compiler.h>
30 #include <Main/fg_props.hxx>
31 #include "voice.hxx"
32
33 #if defined(ENABLE_FLITE)
34 #include "flitevoice.hxx"
35 #endif
36
37 #define VOICE "/sim/sound/voices"
38
39 using std::string;
40
41 class FGFestivalVoice : public FGVoiceMgr::FGVoice {
42 public:
43   FGFestivalVoice(FGVoiceMgr *, const SGPropertyNode_ptr);
44   virtual ~FGFestivalVoice();
45   virtual void speak( const string & msg );
46   virtual void update();
47   void setVolume(double);
48   void setPitch(double);
49   void setSpeed(double);
50
51 private:
52   SGSocket *_sock;
53   double _volume;
54   double _pitch;
55   double _speed;
56   SGPropertyNode_ptr _volumeNode;
57   SGPropertyNode_ptr _pitchNode;
58   SGPropertyNode_ptr _speedNode;
59 };
60
61 /// MANAGER ///
62
63 FGVoiceMgr::FGVoiceMgr() :
64 #if defined(ENABLE_THREADS)
65   _thread(NULL),
66 #endif
67         _host(fgGetString(VOICE "/host", "localhost")),
68         _port(fgGetString(VOICE "/port", "1314")),
69         _enabled(fgGetBool(VOICE "/enabled", false)),
70         _pausedNode(fgGetNode("/sim/sound/working", true)),
71         _paused(false)
72 {
73 }
74
75
76 FGVoiceMgr::~FGVoiceMgr()
77 {
78 }
79
80
81 void FGVoiceMgr::init()
82 {
83         if (!_enabled)
84                 return;
85
86 #if defined(ENABLE_THREADS)
87   _thread = new FGVoiceThread(this);
88 #endif
89
90         SGPropertyNode *base = fgGetNode(VOICE, true);
91         vector<SGPropertyNode_ptr> voices = base->getChildren("voice");
92   for (unsigned int i = 0; i < voices.size(); i++) {
93     SGPropertyNode_ptr voice = voices[i];
94     if( voice->getBoolValue("festival", false ) ) {
95       try {
96         SG_LOG(SG_ALL,SG_ALERT,"creating festival voice" );
97         _voices.push_back(new FGFestivalVoice(this, voice));
98       } catch (const std::string& s) {
99         SG_LOG(SG_SOUND, SG_ALERT, "VOICE: " << s);
100       }
101     } else {
102 #if defined(ENABLE_FLITE)
103       SG_LOG(SG_ALL,SG_ALERT,"creating flite voice" );
104       _voices.push_back(new FGFLITEVoice(this, voice));
105 #else
106       SG_LOG(SG_ALL,SG_ALERT,"non festival voice not supported." );
107 #endif
108     }
109   }
110
111 #if defined(ENABLE_THREADS)
112         _thread->setProcessorAffinity(1);
113         _thread->start();
114 #endif
115 }
116
117 void FGVoiceMgr::shutdown()
118 {
119 #if defined(ENABLE_THREADS)
120   _thread->cancel();
121   _thread->join();
122   delete _thread;
123   _thread = NULL;
124 #endif
125
126   for( std::vector<FGVoice*>::iterator it = _voices.begin(); it != _voices.end(); ++it )
127     delete *it;
128 }
129
130
131 void FGVoiceMgr::update(double)
132 {
133         if (!_enabled)
134                 return;
135
136         _paused = !_pausedNode->getBoolValue();
137         for (unsigned int i = 0; i < _voices.size(); i++) {
138                 _voices[i]->update();
139 #if !defined(ENABLE_THREADS)
140                 _voices[i]->speak();
141 #endif
142         }
143 }
144
145
146
147
148 /// VOICE ///
149
150 FGFestivalVoice::FGFestivalVoice(FGVoiceMgr *mgr, const SGPropertyNode_ptr node) :
151   FGVoice(mgr),
152         _volumeNode(node->getNode("volume", true)),
153         _pitchNode(node->getNode("pitch", true)),
154         _speedNode(node->getNode("speed", true))
155 {
156         SG_LOG(SG_SOUND, SG_INFO, "VOICE: adding `" << node->getStringValue("desc", "<unnamed>")
157                         << "' voice");
158         const string &host = _mgr->_host;
159         const string &port = _mgr->_port;
160
161         _sock = new SGSocket(host, port, "tcp");
162         _sock->set_timeout(6000);
163         if (!_sock->open(SG_IO_OUT))
164                 throw string("no connection to `") + host + ':' + port + '\'';
165
166         {
167                 _sock->writestring("(SayText \"\")\015\012");
168                 char buf[4];
169                 int len = _sock->read(buf, 3);
170                 if (len != 3 || buf[0] != 'L' || buf[1] != 'P')
171                         throw string("unexpected or no response from `") + host + ':' + port
172                                         + "'. Either it's not\n       Festival listening,"
173                                         " or Festival couldn't open a sound device.";
174
175                 SG_LOG(SG_SOUND, SG_INFO, "VOICE: connection to Festival server on `"
176                                 << host << ':' << port << "' established");
177
178                 setVolume(_volume = _volumeNode->getDoubleValue());
179                 setPitch(_pitch = _pitchNode->getDoubleValue());
180                 setSpeed(_speed = _speedNode->getDoubleValue());
181         }
182
183         string preamble = node->getStringValue("preamble", "");
184         if (!preamble.empty())
185                 pushMessage(preamble);
186   node->getNode("text", true)->addChangeListener(this);
187 }
188
189
190 FGFestivalVoice::~FGFestivalVoice()
191 {
192         _sock->close();
193         delete _sock;
194 }
195
196
197 void FGVoiceMgr::FGVoice::pushMessage( const string & m)
198 {
199         _msg.push(m);
200 #if defined(ENABLE_THREADS)
201         _mgr->_thread->wake_up();
202 #endif
203 }
204
205 bool FGVoiceMgr::FGVoice::speak(void)
206 {
207   if (_msg.empty())
208     return false;
209
210   const string s = _msg.front();
211   _msg.pop();
212
213   speak(s);
214
215   return !_msg.empty();
216 }
217
218 void FGFestivalVoice::speak( const string & msg )
219 {
220   if( msg.empty() )
221     return;
222
223   string s;
224
225   if( msg[0] == '(' ) {
226     s = msg;
227   } else {
228     s.append("(SayText \"");
229     s.append(msg).append("\")");
230   }
231
232   s.append("\015\012");
233         _sock->writestring(s.c_str());
234 }
235
236
237 void FGFestivalVoice::update(void)
238 {
239                 double d;
240                 d = _volumeNode->getDoubleValue();
241                 if (d != _volume)
242                         setVolume(_volume = d);
243                 d = _pitchNode->getDoubleValue();
244                 if (d != _pitch)
245                         setPitch(_pitch = d);
246                 d = _speedNode->getDoubleValue();
247                 if (d != _speed)
248                         setSpeed(_speed = d);
249 }
250
251
252 void FGFestivalVoice::setVolume(double d)
253 {
254         std::ostringstream s;
255         s << "(set! default_after_synth_hooks (list (lambda (utt)"
256                         "(utt.wave.rescale utt " << d << " t))))";
257         pushMessage(s.str());
258 }
259
260
261 void FGFestivalVoice::setPitch(double d)
262 {
263         std::ostringstream s;
264         s << "(set! int_lr_params '((target_f0_mean " << d <<
265                         ")(target_f0_std 14)(model_f0_mean 170)"
266                         "(model_f0_std 34)))";
267         pushMessage(s.str());
268 }
269
270
271 void FGFestivalVoice::setSpeed(double d)
272 {
273         std::ostringstream s;
274         s << "(Parameter.set 'Duration_Stretch " << d << ')';
275         pushMessage(s.str());
276 }
277
278 /// THREAD ///
279
280 #if defined(ENABLE_THREADS)
281 void FGVoiceMgr::FGVoiceThread::run(void)
282 {
283         while (1) {
284                 bool busy = false;
285                 for (unsigned int i = 0; i < _mgr->_voices.size(); i++)
286                         busy |= _mgr->_voices[i]->speak();
287
288                 if (!busy)
289                         wait_for_jobs();
290         }
291 }
292 #endif
293
294
295
296
297 /// LISTENER ///
298
299 void FGVoiceMgr::FGVoice::valueChanged(SGPropertyNode *node)
300 {
301         if (_mgr->_paused)
302                 return;
303
304         const string s = node->getStringValue();
305         //cerr << "\033[31;1mBEFORE [" << s << "]\033[m" << endl;
306
307         string m;
308         for (unsigned int i = 0; i < s.size(); i++) {
309                 char c = s[i];
310                 if (c == '\n' || c == '\r' || c == '\t')
311                         m += ' ';
312                 else if (!isprint(c))
313                         continue;
314                 else if (c == '\\' || c == '"')
315                         m += '\\', m += c;
316                 else if (c == '|' || c == '_')
317                         m += ' ';       // don't say "vertical bar" or "underscore"
318                 else if (c == '&')
319                         m += " and ";
320         else if (c == '>' || c == '<')
321             m += ' ';      // don't say "greater than" or "less than" either
322                 else if (c == '{') {
323                         while (i < s.size())
324                                 if (s[++i] == '|')
325                                         break;
326                 } else if (c == '}')
327                         ;
328                 else
329                         m += c;
330         }
331         //cerr << "\033[31;1mAFTER [" << m << "]\033[m" << endl;
332
333         pushMessage(m);
334 }
335
336