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