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 #if defined(ENABLE_FLITE)
34 #include "flitevoice.hxx"
37 #define VOICE "/sim/sound/voices"
41 class FGFestivalVoice : public FGVoiceMgr::FGVoice {
43 FGFestivalVoice(FGVoiceMgr *, const SGPropertyNode_ptr);
44 virtual ~FGFestivalVoice();
45 virtual void speak( const string & msg );
46 virtual void update();
47 void setVolume(double);
48 void setPitch(double);
49 void setSpeed(double);
56 SGPropertyNode_ptr _volumeNode;
57 SGPropertyNode_ptr _pitchNode;
58 SGPropertyNode_ptr _speedNode;
63 FGVoiceMgr::FGVoiceMgr() :
64 #if defined(ENABLE_THREADS)
67 _host(fgGetString(VOICE "/host", "localhost")),
68 _port(fgGetString(VOICE "/port", "1314")),
69 _enabled(fgGetBool(VOICE "/enabled", false)),
70 _pausedNode(fgGetNode("/sim/sound/working", true)),
76 FGVoiceMgr::~FGVoiceMgr()
81 void FGVoiceMgr::init()
86 #if defined(ENABLE_THREADS)
87 _thread = new FGVoiceThread(this);
90 SGPropertyNode *base = fgGetNode(VOICE, true);
91 vector<SGPropertyNode_ptr> voices = base->getChildren("voice");
92 for (unsigned int i = 0; i < voices.size(); i++) {
93 SGPropertyNode_ptr voice = voices[i];
94 if( voice->getBoolValue("festival", false ) ) {
96 SG_LOG(SG_ALL,SG_ALERT,"creating festival voice" );
97 _voices.push_back(new FGFestivalVoice(this, voice));
98 } catch (const std::string& s) {
99 SG_LOG(SG_SOUND, SG_ALERT, "VOICE: " << s);
102 #if defined(ENABLE_FLITE)
103 SG_LOG(SG_ALL,SG_ALERT,"creating flite voice" );
104 _voices.push_back(new FGFLITEVoice(this, voice));
106 SG_LOG(SG_ALL,SG_ALERT,"non festival voice not supported." );
111 #if defined(ENABLE_THREADS)
112 _thread->setProcessorAffinity(1);
117 void FGVoiceMgr::shutdown()
119 #if defined(ENABLE_THREADS)
120 SG_LOG(SG_ALL,SG_ALERT,"FGVoiceMgr::shutdown");
129 for( std::vector<FGVoice*>::iterator it = _voices.begin(); it != _voices.end(); ++it )
134 void FGVoiceMgr::update(double)
139 _paused = !_pausedNode->getBoolValue();
140 for (unsigned int i = 0; i < _voices.size(); i++) {
141 _voices[i]->update();
142 #if !defined(ENABLE_THREADS)
153 FGFestivalVoice::FGFestivalVoice(FGVoiceMgr *mgr, const SGPropertyNode_ptr node) :
155 _volumeNode(node->getNode("volume", true)),
156 _pitchNode(node->getNode("pitch", true)),
157 _speedNode(node->getNode("speed", true))
159 SG_LOG(SG_SOUND, SG_INFO, "VOICE: adding `" << node->getStringValue("desc", "<unnamed>")
161 const string &host = _mgr->_host;
162 const string &port = _mgr->_port;
164 _sock = new SGSocket(host, port, "tcp");
165 _sock->set_timeout(6000);
166 if (!_sock->open(SG_IO_OUT))
167 throw string("no connection to `") + host + ':' + port + '\'';
170 _sock->writestring("(SayText \"\")\015\012");
172 int len = _sock->read(buf, 3);
173 if (len != 3 || buf[0] != 'L' || buf[1] != 'P')
174 throw string("unexpected or no response from `") + host + ':' + port
175 + "'. Either it's not\n Festival listening,"
176 " or Festival couldn't open a sound device.";
178 SG_LOG(SG_SOUND, SG_INFO, "VOICE: connection to Festival server on `"
179 << host << ':' << port << "' established");
181 setVolume(_volume = _volumeNode->getDoubleValue());
182 setPitch(_pitch = _pitchNode->getDoubleValue());
183 setSpeed(_speed = _speedNode->getDoubleValue());
186 string preamble = node->getStringValue("preamble", "");
187 if (!preamble.empty())
188 pushMessage(preamble);
189 node->getNode("text", true)->addChangeListener(this);
193 FGFestivalVoice::~FGFestivalVoice()
200 void FGVoiceMgr::FGVoice::pushMessage( const string & m)
203 #if defined(ENABLE_THREADS)
204 _mgr->_thread->wake_up();
208 bool FGVoiceMgr::FGVoice::speak(void)
213 const string s = _msg.front();
218 return !_msg.empty();
221 void FGFestivalVoice::speak( const string & msg )
228 if( msg[0] == '(' ) {
231 s.append("(SayText \"");
232 s.append(msg).append("\")");
235 s.append("\015\012");
236 _sock->writestring(s.c_str());
240 void FGFestivalVoice::update(void)
243 d = _volumeNode->getDoubleValue();
245 setVolume(_volume = d);
246 d = _pitchNode->getDoubleValue();
248 setPitch(_pitch = d);
249 d = _speedNode->getDoubleValue();
251 setSpeed(_speed = d);
255 void FGFestivalVoice::setVolume(double d)
257 std::ostringstream s;
258 s << "(set! default_after_synth_hooks (list (lambda (utt)"
259 "(utt.wave.rescale utt " << d << " t))))";
260 pushMessage(s.str());
264 void FGFestivalVoice::setPitch(double d)
266 std::ostringstream s;
267 s << "(set! int_lr_params '((target_f0_mean " << d <<
268 ")(target_f0_std 14)(model_f0_mean 170)"
269 "(model_f0_std 34)))";
270 pushMessage(s.str());
274 void FGFestivalVoice::setSpeed(double d)
276 std::ostringstream s;
277 s << "(Parameter.set 'Duration_Stretch " << d << ')';
278 pushMessage(s.str());
283 #if defined(ENABLE_THREADS)
284 void FGVoiceMgr::FGVoiceThread::run(void)
288 for (unsigned int i = 0; i < _mgr->_voices.size(); i++)
289 busy |= _mgr->_voices[i]->speak();
302 void FGVoiceMgr::FGVoice::valueChanged(SGPropertyNode *node)
307 const string s = node->getStringValue();
308 //cerr << "\033[31;1mBEFORE [" << s << "]\033[m" << endl;
311 for (unsigned int i = 0; i < s.size(); i++) {
313 if (c == '\n' || c == '\r' || c == '\t')
315 else if (!isprint(c))
317 else if (c == '\\' || c == '"')
319 else if (c == '|' || c == '_')
320 m += ' '; // don't say "vertical bar" or "underscore"
323 else if (c == '>' || c == '<')
324 m += ' '; // don't say "greater than" or "less than" either
334 //cerr << "\033[31;1mAFTER [" << m << "]\033[m" << endl;