]> git.mxchange.org Git - flightgear.git/blob - src/Sound/voice.cxx
MapWidget: make use of the new POI system and display cities on the map.
[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 #define VOICE "/sim/sound/voices"
34
35 using std::string;
36
37 /// MANAGER ///
38
39 FGVoiceMgr::FGVoiceMgr() :
40         _host(fgGetString(VOICE "/host", "localhost")),
41         _port(fgGetString(VOICE "/port", "1314")),
42         _enabled(fgGetBool(VOICE "/enabled", false)),
43         _pausedNode(fgGetNode("/sim/sound/working", true))
44 {
45 #if defined(ENABLE_THREADS)
46         if (!_enabled)
47                 return;
48         _thread = new FGVoiceThread(this);
49 #endif
50 }
51
52
53 FGVoiceMgr::~FGVoiceMgr()
54 {
55 #if defined(ENABLE_THREADS)
56         if (!_enabled)
57                 return;
58         _thread->cancel();
59         _thread->join();
60         delete _thread;
61 #endif
62 }
63
64
65 void FGVoiceMgr::init()
66 {
67         if (!_enabled)
68                 return;
69
70         SGPropertyNode *base = fgGetNode(VOICE, true);
71         vector<SGPropertyNode_ptr> voices = base->getChildren("voice");
72         try {
73                 for (unsigned int i = 0; i < voices.size(); i++)
74                         _voices.push_back(new FGVoice(this, voices[i]));
75         } catch (const std::string& s) {
76                 SG_LOG(SG_SOUND, SG_ALERT, "VOICE: " << s);
77         }
78
79 #if defined(ENABLE_THREADS)
80         _thread->setProcessorAffinity(1);
81         _thread->start();
82 #endif
83 }
84
85
86 void FGVoiceMgr::update(double)
87 {
88         if (!_enabled)
89                 return;
90
91         _paused = !_pausedNode->getBoolValue();
92         for (unsigned int i = 0; i < _voices.size(); i++) {
93                 _voices[i]->update();
94 #if !defined(ENABLE_THREADS)
95                 _voices[i]->speak();
96 #endif
97         }
98
99 }
100
101
102
103
104 /// VOICE ///
105
106 FGVoiceMgr::FGVoice::FGVoice(FGVoiceMgr *mgr, const SGPropertyNode_ptr node) :
107         _volumeNode(node->getNode("volume", true)),
108         _pitchNode(node->getNode("pitch", true)),
109         _speedNode(node->getNode("speed", true)),
110         _festival(node->getBoolValue("festival", true)),
111         _mgr(mgr)
112 {
113         SG_LOG(SG_SOUND, SG_INFO, "VOICE: adding `" << node->getStringValue("desc", "<unnamed>")
114                         << "' voice");
115         const string &host = _mgr->_host;
116         const string &port = _mgr->_port;
117
118         _sock = new SGSocket(host, port, "tcp");
119         _sock->set_timeout(6000);
120         if (!_sock->open(SG_IO_OUT))
121                 throw string("no connection to `") + host + ':' + port + '\'';
122
123         if (_festival) {
124                 _sock->writestring("(SayText \"\")\015\012");
125                 char buf[4];
126                 int len = _sock->read(buf, 3);
127                 if (len != 3 || buf[0] != 'L' || buf[1] != 'P')
128                         throw string("unexpected or no response from `") + host + ':' + port
129                                         + "'. Either it's not\n       Festival listening,"
130                                         " or Festival couldn't open a sound device.";
131
132                 SG_LOG(SG_SOUND, SG_INFO, "VOICE: connection to Festival server on `"
133                                 << host << ':' << port << "' established");
134
135                 setVolume(_volume = _volumeNode->getDoubleValue());
136                 setPitch(_pitch = _pitchNode->getDoubleValue());
137                 setSpeed(_speed = _speedNode->getDoubleValue());
138         }
139
140         string preamble = node->getStringValue("preamble", "");
141         if (!preamble.empty())
142                 pushMessage(preamble);
143
144         node->getNode("text", true)->addChangeListener(new FGVoiceListener(this));
145 }
146
147
148 FGVoiceMgr::FGVoice::~FGVoice()
149 {
150         _sock->close();
151         delete _sock;
152 }
153
154
155 void FGVoiceMgr::FGVoice::pushMessage(string m)
156 {
157         _msg.push(m + "\015\012");
158 #if defined(ENABLE_THREADS)
159         _mgr->_thread->wake_up();
160 #endif
161 }
162
163
164 bool FGVoiceMgr::FGVoice::speak(void)
165 {
166         if (_msg.empty())
167                 return false;
168
169         const string s = _msg.front();
170         _msg.pop();
171         _sock->writestring(s.c_str());
172         return !_msg.empty();
173 }
174
175
176 void FGVoiceMgr::FGVoice::update(void)
177 {
178         if (_festival) {
179                 double d;
180                 d = _volumeNode->getDoubleValue();
181                 if (d != _volume)
182                         setVolume(_volume = d);
183                 d = _pitchNode->getDoubleValue();
184                 if (d != _pitch)
185                         setPitch(_pitch = d);
186                 d = _speedNode->getDoubleValue();
187                 if (d != _speed)
188                         setSpeed(_speed = d);
189         }
190 }
191
192
193 void FGVoiceMgr::FGVoice::setVolume(double d)
194 {
195         std::ostringstream s;
196         s << "(set! default_after_synth_hooks (list (lambda (utt)"
197                         "(utt.wave.rescale utt " << d << " t))))";
198         pushMessage(s.str());
199 }
200
201
202 void FGVoiceMgr::FGVoice::setPitch(double d)
203 {
204         std::ostringstream s;
205         s << "(set! int_lr_params '((target_f0_mean " << d <<
206                         ")(target_f0_std 14)(model_f0_mean 170)"
207                         "(model_f0_std 34)))";
208         pushMessage(s.str());
209 }
210
211
212 void FGVoiceMgr::FGVoice::setSpeed(double d)
213 {
214         std::ostringstream s;
215         s << "(Parameter.set 'Duration_Stretch " << d << ')';
216         pushMessage(s.str());
217 }
218
219
220
221
222 /// THREAD ///
223
224 #if defined(ENABLE_THREADS)
225 void FGVoiceMgr::FGVoiceThread::run(void)
226 {
227         while (1) {
228                 bool busy = false;
229                 for (unsigned int i = 0; i < _mgr->_voices.size(); i++)
230                         busy |= _mgr->_voices[i]->speak();
231
232                 if (!busy)
233                         wait_for_jobs();
234         }
235 }
236 #endif
237
238
239
240
241 /// LISTENER ///
242
243 void FGVoiceMgr::FGVoice::FGVoiceListener::valueChanged(SGPropertyNode *node)
244 {
245         if (_voice->_mgr->_paused)
246                 return;
247
248         const string s = node->getStringValue();
249         //cerr << "\033[31;1mBEFORE [" << s << "]\033[m" << endl;
250
251         string m;
252         for (unsigned int i = 0; i < s.size(); i++) {
253                 char c = s[i];
254                 if (c == '\n' || c == '\r' || c == '\t')
255                         m += ' ';
256                 else if (!isprint(c))
257                         continue;
258                 else if (c == '\\' || c == '"')
259                         m += '\\', m += c;
260                 else if (c == '|' || c == '_')
261                         m += ' ';       // don't say "vertical bar" or "underscore"
262                 else if (c == '&')
263                         m += " and ";
264                 else if (c == '{') {
265                         while (i < s.size())
266                                 if (s[++i] == '|')
267                                         break;
268                 } else if (c == '}')
269                         ;
270                 else
271                         m += c;
272         }
273         //cerr << "\033[31;1mAFTER [" << m << "]\033[m" << endl;
274         if (_voice->_festival)
275                 m = string("(SayText \"") + m + "\")";
276
277         _voice->pushMessage(m);
278 }
279
280