summaryrefslogtreecommitdiff
path: root/rbutil/rbutilqt/tts.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'rbutil/rbutilqt/tts.cpp')
-rw-r--r--rbutil/rbutilqt/tts.cpp494
1 files changed, 248 insertions, 246 deletions
diff --git a/rbutil/rbutilqt/tts.cpp b/rbutil/rbutilqt/tts.cpp
index 48555cc114..68f169e875 100644
--- a/rbutil/rbutilqt/tts.cpp
+++ b/rbutil/rbutilqt/tts.cpp
@@ -50,17 +50,17 @@ TTSBase* TTSBase::getTTS(QString ttsName)
50 if(ttsName == "sapi") 50 if(ttsName == "sapi")
51 { 51 {
52 tts = new TTSSapi(); 52 tts = new TTSSapi();
53 ttsCache[ttsName] = tts; 53 ttsCache[ttsName] = tts;
54 return tts; 54 return tts;
55 } 55 }
56 else 56 else
57#endif 57#endif
58#if defined(Q_OS_LINUX) 58#if defined(Q_OS_LINUX)
59 if (ttsName == "festival") 59 if (ttsName == "festival")
60 { 60 {
61 tts = new TTSFestival(); 61 tts = new TTSFestival();
62 ttsCache[ttsName] = tts; 62 ttsCache[ttsName] = tts;
63 return tts; 63 return tts;
64 } 64 }
65 else 65 else
66#endif 66#endif
@@ -104,30 +104,30 @@ TTSBase::TTSBase(): QObject()
104TTSExes::TTSExes(QString name) : TTSBase() 104TTSExes::TTSExes(QString name) : TTSBase()
105{ 105{
106 m_name = name; 106 m_name = name;
107 107
108 m_TemplateMap["espeak"] = "\"%exe\" %options -w \"%wavfile\" \"%text\""; 108 m_TemplateMap["espeak"] = "\"%exe\" %options -w \"%wavfile\" \"%text\"";
109 m_TemplateMap["flite"] = "\"%exe\" %options -o \"%wavfile\" -t \"%text\""; 109 m_TemplateMap["flite"] = "\"%exe\" %options -o \"%wavfile\" -t \"%text\"";
110 m_TemplateMap["swift"] = "\"%exe\" %options -o \"%wavfile\" \"%text\""; 110 m_TemplateMap["swift"] = "\"%exe\" %options -o \"%wavfile\" \"%text\"";
111 111
112} 112}
113 113
114void TTSExes::setCfg(RbSettings* sett) 114void TTSExes::setCfg(RbSettings* sett)
115{ 115{
116 // call function of base class 116 // call function of base class
117 TTSBase::setCfg(sett); 117 TTSBase::setCfg(sett);
118 118
119 // if the config isnt OK, try to autodetect 119 // if the config isnt OK, try to autodetect
120 if(!configOk()) 120 if(!configOk())
121 { 121 {
122 QString exepath; 122 QString exepath;
123 //try autodetect tts 123 //try autodetect tts
124#if defined(Q_OS_LINUX) || defined(Q_OS_MACX) || defined(Q_OS_OPENBSD) 124#if defined(Q_OS_LINUX) || defined(Q_OS_MACX) || defined(Q_OS_OPENBSD)
125 QStringList path = QString(getenv("PATH")).split(":", QString::SkipEmptyParts); 125 QStringList path = QString(getenv("PATH")).split(":", QString::SkipEmptyParts);
126#elif defined(Q_OS_WIN) 126#elif defined(Q_OS_WIN)
127 QStringList path = QString(getenv("PATH")).split(";", QString::SkipEmptyParts); 127 QStringList path = QString(getenv("PATH")).split(";", QString::SkipEmptyParts);
128#endif 128#endif
129 qDebug() << path; 129 qDebug() << path;
130 for(int i = 0; i < path.size(); i++) 130 for(int i = 0; i < path.size(); i++)
131 { 131 {
132 QString executable = QDir::fromNativeSeparators(path.at(i)) + "/" + m_name; 132 QString executable = QDir::fromNativeSeparators(path.at(i)) + "/" + m_name;
133#if defined(Q_OS_WIN) 133#if defined(Q_OS_WIN)
@@ -145,14 +145,14 @@ void TTSExes::setCfg(RbSettings* sett)
145 settings->setTTSPath(m_name,exepath); 145 settings->setTTSPath(m_name,exepath);
146 settings->sync(); 146 settings->sync();
147 } 147 }
148 148
149} 149}
150 150
151bool TTSExes::start(QString *errStr) 151bool TTSExes::start(QString *errStr)
152{ 152{
153 m_TTSexec = settings->ttsPath(m_name); 153 m_TTSexec = settings->ttsPath(m_name);
154 m_TTSOpts = settings->ttsOptions(m_name); 154 m_TTSOpts = settings->ttsOptions(m_name);
155 155
156 m_TTSTemplate = m_TemplateMap.value(m_name); 156 m_TTSTemplate = m_TemplateMap.value(m_name);
157 157
158 QFileInfo tts(m_TTSexec); 158 QFileInfo tts(m_TTSexec);
@@ -169,7 +169,7 @@ bool TTSExes::start(QString *errStr)
169 169
170TTSStatus TTSExes::voice(QString text,QString wavfile, QString *errStr) 170TTSStatus TTSExes::voice(QString text,QString wavfile, QString *errStr)
171{ 171{
172 (void) errStr; 172 (void) errStr;
173 QString execstring = m_TTSTemplate; 173 QString execstring = m_TTSTemplate;
174 174
175 execstring.replace("%exe",m_TTSexec); 175 execstring.replace("%exe",m_TTSexec);
@@ -187,7 +187,7 @@ void TTSExes::showCfg()
187#ifndef CONSOLE 187#ifndef CONSOLE
188 TTSExesGui gui; 188 TTSExesGui gui;
189#else 189#else
190 TTSExesGuiCli gui; 190 TTSExesGuiCli gui;
191#endif 191#endif
192 gui.setCfg(settings); 192 gui.setCfg(settings);
193 gui.showCfg(m_name); 193 gui.showCfg(m_name);
@@ -196,10 +196,10 @@ void TTSExes::showCfg()
196bool TTSExes::configOk() 196bool TTSExes::configOk()
197{ 197{
198 QString path = settings->ttsPath(m_name); 198 QString path = settings->ttsPath(m_name);
199 199
200 if (QFileInfo(path).exists()) 200 if (QFileInfo(path).exists())
201 return true; 201 return true;
202 202
203 return false; 203 return false;
204} 204}
205 205
@@ -215,24 +215,24 @@ TTSSapi::TTSSapi() : TTSBase()
215 215
216 216
217bool TTSSapi::start(QString *errStr) 217bool TTSSapi::start(QString *errStr)
218{ 218{
219 219
220 m_TTSOpts = settings->ttsOptions("sapi"); 220 m_TTSOpts = settings->ttsOptions("sapi");
221 m_TTSLanguage =settings->ttsLang("sapi"); 221 m_TTSLanguage =settings->ttsLang("sapi");
222 m_TTSVoice=settings->ttsVoice("sapi"); 222 m_TTSVoice=settings->ttsVoice("sapi");
223 m_TTSSpeed=QString("%1").arg(settings->ttsSpeed("sapi")); 223 m_TTSSpeed=QString("%1").arg(settings->ttsSpeed("sapi"));
224 m_sapi4 = settings->ttsUseSapi4(); 224 m_sapi4 = settings->ttsUseSapi4();
225 225
226 QFile::remove(QDir::tempPath() +"/sapi_voice.vbs"); 226 QFile::remove(QDir::tempPath() +"/sapi_voice.vbs");
227 QFile::copy(":/builtin/sapi_voice.vbs",QDir::tempPath() + "/sapi_voice.vbs"); 227 QFile::copy(":/builtin/sapi_voice.vbs",QDir::tempPath() + "/sapi_voice.vbs");
228 m_TTSexec = QDir::tempPath() +"/sapi_voice.vbs"; 228 m_TTSexec = QDir::tempPath() +"/sapi_voice.vbs";
229 229
230 QFileInfo tts(m_TTSexec); 230 QFileInfo tts(m_TTSexec);
231 if(!tts.exists()) 231 if(!tts.exists())
232 { 232 {
233 *errStr = tr("Could not copy the Sapi-script"); 233 *errStr = tr("Could not copy the Sapi-script");
234 return false; 234 return false;
235 } 235 }
236 // create the voice process 236 // create the voice process
237 QString execstring = m_TTSTemplate; 237 QString execstring = m_TTSTemplate;
238 execstring.replace("%exe",m_TTSexec); 238 execstring.replace("%exe",m_TTSexec);
@@ -240,31 +240,31 @@ bool TTSSapi::start(QString *errStr)
240 execstring.replace("%lang",m_TTSLanguage); 240 execstring.replace("%lang",m_TTSLanguage);
241 execstring.replace("%voice",m_TTSVoice); 241 execstring.replace("%voice",m_TTSVoice);
242 execstring.replace("%speed",m_TTSSpeed); 242 execstring.replace("%speed",m_TTSSpeed);
243 243
244 if(m_sapi4) 244 if(m_sapi4)
245 execstring.append(" /sapi4 "); 245 execstring.append(" /sapi4 ");
246 246
247 qDebug() << "init" << execstring; 247 qDebug() << "init" << execstring;
248 voicescript = new QProcess(NULL); 248 voicescript = new QProcess(NULL);
249 //connect(voicescript,SIGNAL(readyReadStandardError()),this,SLOT(error())); 249 //connect(voicescript,SIGNAL(readyReadStandardError()),this,SLOT(error()));
250 250
251 voicescript->start(execstring); 251 voicescript->start(execstring);
252 if(!voicescript->waitForStarted()) 252 if(!voicescript->waitForStarted())
253 { 253 {
254 *errStr = tr("Could not start the Sapi-script"); 254 *errStr = tr("Could not start the Sapi-script");
255 return false; 255 return false;
256 } 256 }
257 257
258 if(!voicescript->waitForReadyRead(300)) 258 if(!voicescript->waitForReadyRead(300))
259 { 259 {
260 *errStr = voicescript->readAllStandardError(); 260 *errStr = voicescript->readAllStandardError();
261 if(*errStr != "") 261 if(*errStr != "")
262 return false; 262 return false;
263 } 263 }
264 264
265 voicestream = new QTextStream(voicescript); 265 voicestream = new QTextStream(voicescript);
266 voicestream->setCodec("UTF16-LE"); 266 voicestream->setCodec("UTF16-LE");
267 267
268 return true; 268 return true;
269} 269}
270 270
@@ -272,31 +272,31 @@ bool TTSSapi::start(QString *errStr)
272QStringList TTSSapi::getVoiceList(QString language) 272QStringList TTSSapi::getVoiceList(QString language)
273{ 273{
274 QStringList result; 274 QStringList result;
275 275
276 QFile::copy(":/builtin/sapi_voice.vbs",QDir::tempPath() + "/sapi_voice.vbs"); 276 QFile::copy(":/builtin/sapi_voice.vbs",QDir::tempPath() + "/sapi_voice.vbs");
277 m_TTSexec = QDir::tempPath() +"/sapi_voice.vbs"; 277 m_TTSexec = QDir::tempPath() +"/sapi_voice.vbs";
278 278
279 QFileInfo tts(m_TTSexec); 279 QFileInfo tts(m_TTSexec);
280 if(!tts.exists()) 280 if(!tts.exists())
281 return result; 281 return result;
282 282
283 // create the voice process 283 // create the voice process
284 QString execstring = "cscript //nologo \"%exe\" /language:%lang /listvoices"; 284 QString execstring = "cscript //nologo \"%exe\" /language:%lang /listvoices";
285 execstring.replace("%exe",m_TTSexec); 285 execstring.replace("%exe",m_TTSexec);
286 execstring.replace("%lang",language); 286 execstring.replace("%lang",language);
287 287
288 if(settings->ttsUseSapi4()) 288 if(settings->ttsUseSapi4())
289 execstring.append(" /sapi4 "); 289 execstring.append(" /sapi4 ");
290 290
291 qDebug() << "init" << execstring; 291 qDebug() << "init" << execstring;
292 voicescript = new QProcess(NULL); 292 voicescript = new QProcess(NULL);
293 voicescript->start(execstring); 293 voicescript->start(execstring);
294 qDebug() << "wait for started"; 294 qDebug() << "wait for started";
295 if(!voicescript->waitForStarted()) 295 if(!voicescript->waitForStarted())
296 return result; 296 return result;
297 voicescript->closeWriteChannel(); 297 voicescript->closeWriteChannel();
298 voicescript->waitForReadyRead(); 298 voicescript->waitForReadyRead();
299 299
300 QString dataRaw = voicescript->readAllStandardError().data(); 300 QString dataRaw = voicescript->readAllStandardError().data();
301 result = dataRaw.split(",",QString::SkipEmptyParts); 301 result = dataRaw.split(",",QString::SkipEmptyParts);
302 result.sort(); 302 result.sort();
@@ -305,15 +305,16 @@ QStringList TTSSapi::getVoiceList(QString language)
305 { 305 {
306 result[i] = result.at(i).simplified(); 306 result[i] = result.at(i).simplified();
307 } 307 }
308 308
309 309
310 delete voicescript; 310 delete voicescript;
311 QFile::setPermissions(QDir::tempPath() +"/sapi_voice.vbs",QFile::ReadOwner |QFile::WriteOwner|QFile::ExeOwner 311 QFile::setPermissions(QDir::tempPath() +"/sapi_voice.vbs",
312 |QFile::ReadUser| QFile::WriteUser| QFile::ExeUser 312 QFile::ReadOwner |QFile::WriteOwner |QFile::ExeOwner
313 |QFile::ReadGroup |QFile::WriteGroup |QFile::ExeGroup 313 |QFile::ReadUser |QFile::WriteUser |QFile::ExeUser
314 |QFile::ReadOther |QFile::WriteOther |QFile::ExeOther ); 314 |QFile::ReadGroup |QFile::WriteGroup |QFile::ExeGroup
315 |QFile::ReadOther |QFile::WriteOther |QFile::ExeOther);
315 QFile::remove(QDir::tempPath() +"/sapi_voice.vbs"); 316 QFile::remove(QDir::tempPath() +"/sapi_voice.vbs");
316 317
317 return result; 318 return result;
318} 319}
319 320
@@ -321,7 +322,7 @@ QStringList TTSSapi::getVoiceList(QString language)
321 322
322TTSStatus TTSSapi::voice(QString text,QString wavfile, QString *errStr) 323TTSStatus TTSSapi::voice(QString text,QString wavfile, QString *errStr)
323{ 324{
324 (void) errStr; 325 (void) errStr;
325 QString query = "SPEAK\t"+wavfile+"\t"+text+"\r\n"; 326 QString query = "SPEAK\t"+wavfile+"\t"+text+"\r\n";
326 qDebug() << "voicing" << query; 327 qDebug() << "voicing" << query;
327 *voicestream << query; 328 *voicestream << query;
@@ -332,17 +333,18 @@ TTSStatus TTSSapi::voice(QString text,QString wavfile, QString *errStr)
332} 333}
333 334
334bool TTSSapi::stop() 335bool TTSSapi::stop()
335{ 336{
336 337
337 *voicestream << "QUIT\r\n"; 338 *voicestream << "QUIT\r\n";
338 voicestream->flush(); 339 voicestream->flush();
339 voicescript->waitForFinished(); 340 voicescript->waitForFinished();
340 delete voicestream; 341 delete voicestream;
341 delete voicescript; 342 delete voicescript;
342 QFile::setPermissions(QDir::tempPath() +"/sapi_voice.vbs",QFile::ReadOwner |QFile::WriteOwner|QFile::ExeOwner 343 QFile::setPermissions(QDir::tempPath() +"/sapi_voice.vbs",
343 |QFile::ReadUser| QFile::WriteUser| QFile::ExeUser 344 QFile::ReadOwner |QFile::WriteOwner |QFile::ExeOwner
344 |QFile::ReadGroup |QFile::WriteGroup |QFile::ExeGroup 345 |QFile::ReadUser |QFile::WriteUser |QFile::ExeUser
345 |QFile::ReadOther |QFile::WriteOther |QFile::ExeOther ); 346 |QFile::ReadGroup |QFile::WriteGroup |QFile::ExeGroup
347 |QFile::ReadOther |QFile::WriteOther |QFile::ExeOther);
346 QFile::remove(QDir::tempPath() +"/sapi_voice.vbs"); 348 QFile::remove(QDir::tempPath() +"/sapi_voice.vbs");
347 return true; 349 return true;
348} 350}
@@ -353,7 +355,7 @@ void TTSSapi::showCfg()
353#ifndef CONSOLE 355#ifndef CONSOLE
354 TTSSapiGui gui(this); 356 TTSSapiGui gui(this);
355#else 357#else
356 TTSSapiGuiCli gui(this); 358 TTSSapiGuiCli gui(this);
357#endif 359#endif
358 gui.setCfg(settings); 360 gui.setCfg(settings);
359 gui.showCfg(); 361 gui.showCfg();
@@ -370,104 +372,104 @@ bool TTSSapi::configOk()
370 **********************************************************************/ 372 **********************************************************************/
371TTSFestival::~TTSFestival() 373TTSFestival::~TTSFestival()
372{ 374{
373 stop(); 375 stop();
374} 376}
375 377
376void TTSFestival::startServer() 378void TTSFestival::startServer()
377{ 379{
378 if(!configOk()) 380 if(!configOk())
379 return; 381 return;
380 382
381 QStringList paths = settings->ttsPath("festival").split(":"); 383 QStringList paths = settings->ttsPath("festival").split(":");
382 384
383 serverProcess.start(QString("%1 --server").arg(paths[0])); 385 serverProcess.start(QString("%1 --server").arg(paths[0]));
384 serverProcess.waitForStarted(); 386 serverProcess.waitForStarted();
385 387
386 queryServer("(getpid)"); 388 queryServer("(getpid)");
387 if(serverProcess.state() == QProcess::Running) 389 if(serverProcess.state() == QProcess::Running)
388 qDebug() << "Festival is up and running"; 390 qDebug() << "Festival is up and running";
389 else 391 else
390 qDebug() << "Festival failed to start"; 392 qDebug() << "Festival failed to start";
391} 393}
392 394
393void TTSFestival::ensureServerRunning() 395void TTSFestival::ensureServerRunning()
394{ 396{
395 if(serverProcess.state() != QProcess::Running) 397 if(serverProcess.state() != QProcess::Running)
396 { 398 {
397 // least common denominator for all the server startup code paths 399 // least common denominator for all the server startup code paths
398 QProgressDialog progressDialog(tr(""), tr(""), 0, 0); 400 QProgressDialog progressDialog(tr(""), tr(""), 0, 0);
399 progressDialog.setWindowTitle(tr("Starting festival")); 401 progressDialog.setWindowTitle(tr("Starting festival"));
400 progressDialog.setModal(true); 402 progressDialog.setModal(true);
401 progressDialog.setLabel(0); 403 progressDialog.setLabel(0);
402 progressDialog.setCancelButton(0); 404 progressDialog.setCancelButton(0);
403 progressDialog.show(); 405 progressDialog.show();
404 406
405 QApplication::processEvents(); // actually show the dialog 407 QApplication::processEvents(); // actually show the dialog
406 408
407 startServer(); 409 startServer();
408 } 410 }
409} 411}
410 412
411bool TTSFestival::start(QString* errStr) 413bool TTSFestival::start(QString* errStr)
412{ 414{
413 (void) errStr; 415 (void) errStr;
414 ensureServerRunning(); 416 ensureServerRunning();
415 if (!settings->ttsVoice("festival").isEmpty()) 417 if (!settings->ttsVoice("festival").isEmpty())
416 queryServer(QString("(voice.select '%1)").arg(settings->ttsVoice("festival"))); 418 queryServer(QString("(voice.select '%1)").arg(settings->ttsVoice("festival")));
417 419
418 return true; 420 return true;
419} 421}
420 422
421bool TTSFestival::stop() 423bool TTSFestival::stop()
422{ 424{
423 serverProcess.terminate(); 425 serverProcess.terminate();
424 serverProcess.kill(); 426 serverProcess.kill();
425 427
426 return true; 428 return true;
427} 429}
428 430
429TTSStatus TTSFestival::voice(QString text, QString wavfile, QString* errStr) 431TTSStatus TTSFestival::voice(QString text, QString wavfile, QString* errStr)
430{ 432{
431 qDebug() << text << "->" << wavfile; 433 qDebug() << text << "->" << wavfile;
432 434
433 QStringList paths = settings->ttsPath("festival").split(":"); 435 QStringList paths = settings->ttsPath("festival").split(":");
434 QString cmd = QString("%1 --server localhost --otype riff --ttw --withlisp --output \"%2\" - ").arg(paths[1]).arg(wavfile); 436 QString cmd = QString("%1 --server localhost --otype riff --ttw --withlisp --output \"%2\" - ").arg(paths[1]).arg(wavfile);
435 qDebug() << cmd; 437 qDebug() << cmd;
436 438
437 QProcess clientProcess; 439 QProcess clientProcess;
438 clientProcess.start(cmd); 440 clientProcess.start(cmd);
439 clientProcess.write(QString("%1.\n").arg(text).toAscii()); 441 clientProcess.write(QString("%1.\n").arg(text).toAscii());
440 clientProcess.waitForBytesWritten(); 442 clientProcess.waitForBytesWritten();
441 clientProcess.closeWriteChannel(); 443 clientProcess.closeWriteChannel();
442 clientProcess.waitForReadyRead(); 444 clientProcess.waitForReadyRead();
443 QString response = clientProcess.readAll(); 445 QString response = clientProcess.readAll();
444 response = response.trimmed(); 446 response = response.trimmed();
445 if(!response.contains("Utterance")) 447 if(!response.contains("Utterance"))
446 { 448 {
447 qDebug() << "Could not voice string: " << response; 449 qDebug() << "Could not voice string: " << response;
448 *errStr = tr("engine could not voice string"); 450 *errStr = tr("engine could not voice string");
449 return Warning; 451 return Warning;
450 /* do not stop the voicing process because of a single string 452 /* do not stop the voicing process because of a single string
451 TODO: needs proper settings */ 453 TODO: needs proper settings */
452 } 454 }
453 clientProcess.closeReadChannel(QProcess::StandardError); 455 clientProcess.closeReadChannel(QProcess::StandardError);
454 clientProcess.closeReadChannel(QProcess::StandardOutput); 456 clientProcess.closeReadChannel(QProcess::StandardOutput);
455 clientProcess.terminate(); 457 clientProcess.terminate();
456 clientProcess.kill(); 458 clientProcess.kill();
457 459
458 return NoError; 460 return NoError;
459} 461}
460 462
461bool TTSFestival::configOk() 463bool TTSFestival::configOk()
462{ 464{
463 QStringList paths = settings->ttsPath("festival").split(":"); 465 QStringList paths = settings->ttsPath("festival").split(":");
464 if(paths.size() != 2) 466 if(paths.size() != 2)
465 return false; 467 return false;
466 bool ret = QFileInfo(paths[0]).isExecutable() && 468 bool ret = QFileInfo(paths[0]).isExecutable() &&
467 QFileInfo(paths[1]).isExecutable(); 469 QFileInfo(paths[1]).isExecutable();
468 if(settings->ttsVoice("festival").size() > 0 && voices.size() > 0) 470 if(settings->ttsVoice("festival").size() > 0 && voices.size() > 0)
469 ret = ret && (voices.indexOf(settings->ttsVoice("festival")) != -1); 471 ret = ret && (voices.indexOf(settings->ttsVoice("festival")) != -1);
470 return ret; 472 return ret;
471} 473}
472 474
473void TTSFestival::showCfg() 475void TTSFestival::showCfg()
@@ -481,138 +483,138 @@ void TTSFestival::showCfg()
481 483
482QStringList TTSFestival::getVoiceList() 484QStringList TTSFestival::getVoiceList()
483{ 485{
484 if(!configOk()) 486 if(!configOk())
485 return QStringList(); 487 return QStringList();
486 488
487 if(voices.size() > 0) 489 if(voices.size() > 0)
488 { 490 {
489 qDebug() << "Using voice cache"; 491 qDebug() << "Using voice cache";
490 return voices; 492 return voices;
491 } 493 }
492 QString response = queryServer("(voice.list)"); 494 QString response = queryServer("(voice.list)");
493 495
494 // get the 2nd line. It should be (<voice_name>, <voice_name>) 496 // get the 2nd line. It should be (<voice_name>, <voice_name>)
495 response = response.mid(response.indexOf('\n') + 1, -1); 497 response = response.mid(response.indexOf('\n') + 1, -1);
496 response = response.left(response.indexOf('\n')).trimmed(); 498 response = response.left(response.indexOf('\n')).trimmed();
497 499
498 voices = response.mid(1, response.size()-2).split(' '); 500 voices = response.mid(1, response.size()-2).split(' ');
499 501
500 voices.sort(); 502 voices.sort();
501 if (voices.size() == 1 && voices[0].size() == 0) 503 if (voices.size() == 1 && voices[0].size() == 0)
502 voices.removeAt(0); 504 voices.removeAt(0);
503 if (voices.size() > 0) 505 if (voices.size() > 0)
504 qDebug() << "Voices: " << voices; 506 qDebug() << "Voices: " << voices;
505 else 507 else
506 qDebug() << "No voices."; 508 qDebug() << "No voices.";
507 return voices; 509 return voices;
508} 510}
509 511
510QString TTSFestival::getVoiceInfo(QString voice) 512QString TTSFestival::getVoiceInfo(QString voice)
511{ 513{
512 if(!configOk()) 514 if(!configOk())
513 return ""; 515 return "";
514 516
515 if(!getVoiceList().contains(voice)) 517 if(!getVoiceList().contains(voice))
516 return ""; 518 return "";
517 519
518 if(voiceDescriptions.contains(voice)) 520 if(voiceDescriptions.contains(voice))
519 return voiceDescriptions[voice]; 521 return voiceDescriptions[voice];
520 522
521 QString response = queryServer(QString("(voice.description '%1)").arg(voice), 3000); 523 QString response = queryServer(QString("(voice.description '%1)").arg(voice), 3000);
522 524
523 if (response == "") 525 if (response == "")
524 { 526 {
525 voiceDescriptions[voice]=tr("No description available"); 527 voiceDescriptions[voice]=tr("No description available");
526 } 528 }
527 else 529 else
528 { 530 {
529 response = response.remove(QRegExp("(description \"*\")", Qt::CaseInsensitive, QRegExp::Wildcard)); 531 response = response.remove(QRegExp("(description \"*\")", Qt::CaseInsensitive, QRegExp::Wildcard));
530 qDebug() << "voiceInfo w/o descr: " << response; 532 qDebug() << "voiceInfo w/o descr: " << response;
531 response = response.remove(')'); 533 response = response.remove(')');
532 QStringList responseLines = response.split('(', QString::SkipEmptyParts); 534 QStringList responseLines = response.split('(', QString::SkipEmptyParts);
533 responseLines.removeAt(0); // the voice name itself 535 responseLines.removeAt(0); // the voice name itself
534 536
535 QString description; 537 QString description;
536 foreach(QString line, responseLines) 538 foreach(QString line, responseLines)
537 { 539 {
538 line = line.remove('('); 540 line = line.remove('(');
539 line = line.simplified(); 541 line = line.simplified();
540 542
541 line[0] = line[0].toUpper(); // capitalize the key 543 line[0] = line[0].toUpper(); // capitalize the key
542 544
543 int firstSpace = line.indexOf(' '); 545 int firstSpace = line.indexOf(' ');
544 if (firstSpace > 0) 546 if (firstSpace > 0)
545 { 547 {
546 line = line.insert(firstSpace, ':'); // add a colon between the key and the value 548 line = line.insert(firstSpace, ':'); // add a colon between the key and the value
547 line[firstSpace+2] = line[firstSpace+2].toUpper(); // capitalize the value 549 line[firstSpace+2] = line[firstSpace+2].toUpper(); // capitalize the value
548 } 550 }
549 551
550 description += line + "\n"; 552 description += line + "\n";
551 } 553 }
552 voiceDescriptions[voice] = description.trimmed(); 554 voiceDescriptions[voice] = description.trimmed();
553 } 555 }
554 return voiceDescriptions[voice]; 556 return voiceDescriptions[voice];
555} 557}
556 558
557QString TTSFestival::queryServer(QString query, int timeout) 559QString TTSFestival::queryServer(QString query, int timeout)
558{ 560{
559 if(!configOk()) 561 if(!configOk())
560 return ""; 562 return "";
561 563
562 ensureServerRunning(); 564 ensureServerRunning();
563 565
564 qDebug() << "queryServer with " << query; 566 qDebug() << "queryServer with " << query;
565 QString response; 567 QString response;
566 568
567 QDateTime endTime; 569 QDateTime endTime;
568 if(timeout > 0) 570 if(timeout > 0)
569 endTime = QDateTime::currentDateTime().addMSecs(timeout); 571 endTime = QDateTime::currentDateTime().addMSecs(timeout);
570 572
571 /* Festival is *extremely* unreliable. Although at this 573 /* Festival is *extremely* unreliable. Although at this
572 * point we are sure that SIOD is accepting commands, 574 * point we are sure that SIOD is accepting commands,
573 * we might end up with an empty response. Hence, the loop. 575 * we might end up with an empty response. Hence, the loop.
574 */ 576 */
575 while(true) 577 while(true)
576 { 578 {
577 QApplication::processEvents(QEventLoop::AllEvents, 50); 579 QApplication::processEvents(QEventLoop::AllEvents, 50);
578 QTcpSocket socket; 580 QTcpSocket socket;
579 581
580 socket.connectToHost("localhost", 1314); 582 socket.connectToHost("localhost", 1314);
581 socket.waitForConnected(); 583 socket.waitForConnected();
582 584
583 if(socket.state() == QAbstractSocket::ConnectedState) 585 if(socket.state() == QAbstractSocket::ConnectedState)
584 { 586 {
585 socket.write(QString("%1\n").arg(query).toAscii()); 587 socket.write(QString("%1\n").arg(query).toAscii());
586 socket.waitForBytesWritten(); 588 socket.waitForBytesWritten();
587 socket.waitForReadyRead(); 589 socket.waitForReadyRead();
588 590
589 response = socket.readAll().trimmed(); 591 response = socket.readAll().trimmed();
590 592
591 if (response != "LP" && response != "") 593 if (response != "LP" && response != "")
592 break; 594 break;
593 } 595 }
594 socket.abort(); 596 socket.abort();
595 socket.disconnectFromHost(); 597 socket.disconnectFromHost();
596 598
597 if(timeout > 0 && QDateTime::currentDateTime() >= endTime) 599 if(timeout > 0 && QDateTime::currentDateTime() >= endTime)
598 return ""; 600 return "";
599 601
600 /* make sure we wait a little as we don't want to flood the server with requests */ 602 /* make sure we wait a little as we don't want to flood the server with requests */
601 QDateTime tmpEndTime = QDateTime::currentDateTime().addMSecs(500); 603 QDateTime tmpEndTime = QDateTime::currentDateTime().addMSecs(500);
602 while(QDateTime::currentDateTime() < tmpEndTime) 604 while(QDateTime::currentDateTime() < tmpEndTime)
603 QApplication::processEvents(QEventLoop::AllEvents); 605 QApplication::processEvents(QEventLoop::AllEvents);
604 } 606 }
605 if(response == "nil") 607 if(response == "nil")
606 return ""; 608 return "";
607 609
608 QStringList lines = response.split('\n'); 610 QStringList lines = response.split('\n');
609 if(lines.size() > 2) 611 if(lines.size() > 2)
610 { 612 {
611 lines.removeFirst(); 613 lines.removeFirst();
612 lines.removeLast(); 614 lines.removeLast();
613 } 615 }
614 else 616 else
615 qDebug() << "Response too short: " << response; 617 qDebug() << "Response too short: " << response;
616 return lines.join("\n"); 618 return lines.join("\n");
617} 619}
618 620