]> git.mxchange.org Git - flightgear.git/blob - src/Sound/voice.cxx
Add support for emissive layers.
[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 <sstream>
24 #include <simgear/compiler.h>
25 #include <Main/fg_props.hxx>
26 #include <Main/globals.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/pause", 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 #endif
56 }
57
58
59 void FGVoiceMgr::init()
60 {
61         if (!_enabled)
62                 return;
63
64         SGPropertyNode *base = fgGetNode(VOICE, true);
65         vector<SGPropertyNode_ptr> voices = base->getChildren("voice");
66         for (unsigned int i = 0; i < voices.size(); i++)
67                 _voices.push_back(new FGVoice(this, voices[i]));
68
69 #if defined(ENABLE_THREADS)
70         _thread->start(1);
71 #endif
72 }
73
74
75 void FGVoiceMgr::update(double)
76 {
77         if (!_enabled)
78                 return;
79
80         _paused = _pausedNode->getBoolValue();
81         for (unsigned int i = 0; i < _voices.size(); i++) {
82                 _voices[i]->update();
83 #if !defined(ENABLE_THREADS)
84                 _voices[i]->speak();
85 #endif
86         }
87
88 }
89
90
91
92
93 /// VOICE ///
94
95 FGVoiceMgr::FGVoice::FGVoice(FGVoiceMgr *mgr, const SGPropertyNode_ptr node) :
96         _volumeNode(node->getNode("volume", true)),
97         _pitchNode(node->getNode("pitch", true)),
98         _speedNode(node->getNode("speed", true)),
99         _festival(node->getBoolValue("festival", true)),
100         _mgr(mgr)
101 {
102         SG_LOG(SG_IO, SG_INFO, "VOICE: adding `" << node->getStringValue("desc", "<unnamed>")
103                         << "' voice");
104         const string &host = _mgr->_host;
105         const string &port = _mgr->_port;
106
107         _sock = new SGSocket(host, port, "tcp");
108         _sock->set_timeout(10000);
109         _connected = _sock->open(SG_IO_OUT);
110         if (!_connected) {
111                 SG_LOG(SG_IO, SG_ALERT, "VOICE: no connection to `"
112                                 << host << ':' << port << '\'');
113                 return;
114         }
115
116         if (_festival) {
117                 _sock->writestring("(SayText \"\")\015\012");
118                 char buf[4];
119                 int len = _sock->read(buf, 3);
120                 if (len != 3 || buf[0] != 'L' || buf[1] != 'P') {
121                         SG_LOG(SG_IO, SG_ALERT, "VOICE: something is listening to "
122                                 << host << ':' << port << "', but it doesn't seem "
123                                 "to be Festival");
124                         _connected = false;
125                         return;
126                 }
127
128                 SG_LOG(SG_IO, SG_BULK, "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 //              cerr << "<nothing to say>" << endl;
164                 return false;
165         }
166
167         const string s = _msg.front();
168         _msg.pop();
169 //      cerr << "POP " << s;
170         _sock->writestring(s.c_str());
171         return !_msg.empty();
172 }
173
174
175 void FGVoiceMgr::FGVoice::update(void)
176 {
177         if (_connected && _festival) {
178                 double d;
179                 d = _volumeNode->getDoubleValue();
180                 if (d != _volume)
181                         setVolume(_volume = d);
182                 d = _pitchNode->getDoubleValue();
183                 if (d != _pitch)
184                         setPitch(_pitch = d);
185                 d = _speedNode->getDoubleValue();
186                 if (d != _speed)
187                         setSpeed(_speed = d);
188         }
189 }
190
191
192 void FGVoiceMgr::FGVoice::setVolume(double d)
193 {
194         std::ostringstream s;
195         s << "(set! default_after_synth_hooks (list (lambda (utt)"
196                         "(utt.wave.rescale utt " << d << " t))))";
197         pushMessage(s.str());
198 }
199
200
201 void FGVoiceMgr::FGVoice::setPitch(double d)
202 {
203         std::ostringstream s;
204         s << "(set! int_lr_params '((target_f0_mean " << d <<
205                         ")(target_f0_std 14)(model_f0_mean 170)"
206                         "(model_f0_std 34)))";
207         pushMessage(s.str());
208 }
209
210
211 void FGVoiceMgr::FGVoice::setSpeed(double d)
212 {
213         std::ostringstream s;
214         s << "(Parameter.set 'Duration_Stretch " << d << ')';
215         pushMessage(s.str());
216 }
217
218
219
220
221 /// THREAD ///
222
223 #if defined(ENABLE_THREADS)
224 void FGVoiceMgr::FGVoiceThread::run(void)
225 {
226         while (1) {
227                 bool busy = false;
228                 for (unsigned int i = 0; i < _mgr->_voices.size(); i++)
229                         busy |= _mgr->_voices[i]->speak();
230
231                 if (!busy)
232                         wait_for_jobs();
233         }
234 }
235 #endif
236
237
238
239
240 /// LISTENER ///
241
242 void FGVoiceMgr::FGVoice::FGVoiceListener::valueChanged(SGPropertyNode *node)
243 {
244         if (_voice->_mgr->_paused)
245                 return;
246
247         const string s = node->getStringValue();
248 //      cerr << "PUSH " << s << endl;
249
250         string m;
251         for (unsigned int i = 0; i < s.size(); i++) {
252                 char c = s[i];
253                 if (!isprint(c))
254                         continue;
255                 else if (c == '"' || c == '\\')
256                         m += '\\' + c;
257                 else if (c == '|' || c == '_')
258                         m += ' ';       // don't say "vertical bar" or "underscore"
259                 else if (c == '&')
260                         m += " and ";
261                 else
262                         m += c;
263         }
264         if (_voice->_festival)
265                 m = string("(SayText \"") + m + "\")";
266
267         _voice->pushMessage(m);
268 }
269
270