1 // speech synthesis interface subsystem
3 // Written by Melchior FRANZ, started February 2006.
5 // Copyright (C) 2006 Melchior FRANZ - mfranz@aon.at
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.
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.
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.
27 #include <Main/globals.hxx>
29 #include <simgear/compiler.h>
30 #include <Main/fg_props.hxx>
33 #define VOICE "/sim/sound/voices"
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))
45 #if defined(ENABLE_THREADS)
48 _thread = new FGVoiceThread(this);
53 FGVoiceMgr::~FGVoiceMgr()
55 #if defined(ENABLE_THREADS)
65 void FGVoiceMgr::init()
70 SGPropertyNode *base = fgGetNode(VOICE, true);
71 vector<SGPropertyNode_ptr> voices = base->getChildren("voice");
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);
79 #if defined(ENABLE_THREADS)
80 _thread->setProcessorAffinity(1);
86 void FGVoiceMgr::update(double)
91 _paused = !_pausedNode->getBoolValue();
92 for (unsigned int i = 0; i < _voices.size(); i++) {
94 #if !defined(ENABLE_THREADS)
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)),
113 SG_LOG(SG_SOUND, SG_INFO, "VOICE: adding `" << node->getStringValue("desc", "<unnamed>")
115 const string &host = _mgr->_host;
116 const string &port = _mgr->_port;
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 + '\'';
124 _sock->writestring("(SayText \"\")\015\012");
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.";
132 SG_LOG(SG_SOUND, SG_INFO, "VOICE: connection to Festival server on `"
133 << host << ':' << port << "' established");
135 setVolume(_volume = _volumeNode->getDoubleValue());
136 setPitch(_pitch = _pitchNode->getDoubleValue());
137 setSpeed(_speed = _speedNode->getDoubleValue());
140 string preamble = node->getStringValue("preamble", "");
141 if (!preamble.empty())
142 pushMessage(preamble);
144 node->getNode("text", true)->addChangeListener(new FGVoiceListener(this));
148 FGVoiceMgr::FGVoice::~FGVoice()
155 void FGVoiceMgr::FGVoice::pushMessage(string m)
157 _msg.push(m + "\015\012");
158 #if defined(ENABLE_THREADS)
159 _mgr->_thread->wake_up();
164 bool FGVoiceMgr::FGVoice::speak(void)
169 const string s = _msg.front();
171 _sock->writestring(s.c_str());
172 return !_msg.empty();
176 void FGVoiceMgr::FGVoice::update(void)
180 d = _volumeNode->getDoubleValue();
182 setVolume(_volume = d);
183 d = _pitchNode->getDoubleValue();
185 setPitch(_pitch = d);
186 d = _speedNode->getDoubleValue();
188 setSpeed(_speed = d);
193 void FGVoiceMgr::FGVoice::setVolume(double d)
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());
202 void FGVoiceMgr::FGVoice::setPitch(double d)
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());
212 void FGVoiceMgr::FGVoice::setSpeed(double d)
214 std::ostringstream s;
215 s << "(Parameter.set 'Duration_Stretch " << d << ')';
216 pushMessage(s.str());
224 #if defined(ENABLE_THREADS)
225 void FGVoiceMgr::FGVoiceThread::run(void)
229 for (unsigned int i = 0; i < _mgr->_voices.size(); i++)
230 busy |= _mgr->_voices[i]->speak();
243 void FGVoiceMgr::FGVoice::FGVoiceListener::valueChanged(SGPropertyNode *node)
245 if (_voice->_mgr->_paused)
248 const string s = node->getStringValue();
249 //cerr << "\033[31;1mBEFORE [" << s << "]\033[m" << endl;
252 for (unsigned int i = 0; i < s.size(); i++) {
254 if (c == '\n' || c == '\r' || c == '\t')
256 else if (!isprint(c))
258 else if (c == '\\' || c == '"')
260 else if (c == '|' || c == '_')
261 m += ' '; // don't say "vertical bar" or "underscore"
273 //cerr << "\033[31;1mAFTER [" << m << "]\033[m" << endl;
274 if (_voice->_festival)
275 m = string("(SayText \"") + m + "\")";
277 _voice->pushMessage(m);