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