summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--utils/rbutilqt/base/ttsfestival.cpp88
1 files changed, 48 insertions, 40 deletions
diff --git a/utils/rbutilqt/base/ttsfestival.cpp b/utils/rbutilqt/base/ttsfestival.cpp
index 4c718de824..fbb8166a9a 100644
--- a/utils/rbutilqt/base/ttsfestival.cpp
+++ b/utils/rbutilqt/base/ttsfestival.cpp
@@ -121,18 +121,14 @@ void TTSFestival::startServer()
121 QString path; 121 QString path;
122 /* currentPath is set by the GUI - if it's set, it is the currently set 122 /* currentPath is set by the GUI - if it's set, it is the currently set
123 path in the configuration GUI; if it's not set, use the saved path */ 123 path in the configuration GUI; if it's not set, use the saved path */
124 if (currentPath == "") 124 if (currentPath.isEmpty())
125 path = RbSettings::subValue("festival-server",RbSettings::TtsPath).toString(); 125 path = RbSettings::subValue("festival-server",RbSettings::TtsPath).toString();
126 else 126 else
127 path = currentPath; 127 path = currentPath;
128 128
129 serverProcess.start(QString("%1 --server").arg(path)); 129 serverProcess.start(path, QStringList("--server"));
130 serverProcess.waitForStarted(); 130 serverProcess.waitForStarted();
131 131
132 /* A friendlier version of a spinlock */
133 while (serverProcess.processId() == 0 && serverProcess.state() != QProcess::Running)
134 QCoreApplication::processEvents(QEventLoop::AllEvents, 50);
135
136 if(serverProcess.state() == QProcess::Running) 132 if(serverProcess.state() == QProcess::Running)
137 LOG_INFO() << "Server is up and running"; 133 LOG_INFO() << "Server is up and running";
138 else 134 else
@@ -174,7 +170,7 @@ bool TTSFestival::start(QString* errStr)
174 } 170 }
175 171
176 if (!running) 172 if (!running)
177 (*errStr) = "Festival could not be started"; 173 (*errStr) = tr("Festival could not be started");
178 return running; 174 return running;
179} 175}
180 176
@@ -192,12 +188,13 @@ TTSStatus TTSFestival::voice(QString text, QString wavfile, QString* errStr)
192 188
193 QString path = RbSettings::subValue("festival-client", 189 QString path = RbSettings::subValue("festival-client",
194 RbSettings::TtsPath).toString(); 190 RbSettings::TtsPath).toString();
195 QString cmd = QString("%1 --server localhost --otype riff --ttw --withlisp" 191 QStringList cmd;
196 " --output \"%2\" --prolog \"%3\" - ").arg(path, wavfile, prologPath); 192 cmd << "--server" << "localhost" << "--otype" << "riff" << "--ttw"
197 LOG_INFO() << "Client cmd:" << cmd; 193 << "--withlisp" << "--output" << wavfile << "--prolog" << prologPath << "-";
194 LOG_INFO() << "Client cmd:" << path << cmd;
198 195
199 QProcess clientProcess; 196 QProcess clientProcess;
200 clientProcess.start(cmd); 197 clientProcess.start(path, cmd);
201 clientProcess.write(QString("%1.\n").arg(text).toLatin1()); 198 clientProcess.write(QString("%1.\n").arg(text).toLatin1());
202 clientProcess.waitForBytesWritten(); 199 clientProcess.waitForBytesWritten();
203 clientProcess.closeWriteChannel(); 200 clientProcess.closeWriteChannel();
@@ -332,6 +329,9 @@ QString TTSFestival::getVoiceInfo(QString voice)
332 329
333QString TTSFestival::queryServer(QString query, int timeout) 330QString TTSFestival::queryServer(QString query, int timeout)
334{ 331{
332 // make sure we always abort at some point.
333 if(timeout == 0)
334 timeout = 60000;
335 if(!configOk()) 335 if(!configOk())
336 return ""; 336 return "";
337 337
@@ -347,66 +347,74 @@ QString TTSFestival::queryServer(QString query, int timeout)
347 return ""; 347 return "";
348 } 348 }
349 349
350 QString response;
351 350
352 QDateTime endTime; 351 QDateTime endTime = QDateTime::currentDateTime().addMSecs(timeout);
353 if(timeout > 0)
354 endTime = QDateTime::currentDateTime().addMSecs(timeout);
355 352
356 /* Festival is *extremely* unreliable. Although at this 353 /* Festival is *extremely* unreliable. Although at this
357 * point we are sure that SIOD is accepting commands, 354 * point we are sure that SIOD is accepting commands,
358 * we might end up with an empty response. Hence, the loop. 355 * we might end up with an empty response. Hence, the loop.
359 */ 356 */
360 while(true) 357 QTcpSocket socket;
358 QString response;
359 while(QDateTime::currentDateTime() < endTime)
361 { 360 {
362 QCoreApplication::processEvents(QEventLoop::AllEvents, 50); 361 QCoreApplication::processEvents(QEventLoop::AllEvents, 50);
363 QTcpSocket socket;
364 362
365 socket.connectToHost("localhost", 1314); 363 if(socket.state() != QAbstractSocket::ConnectedState)
366 socket.waitForConnected();
367
368 if(socket.state() == QAbstractSocket::ConnectedState)
369 { 364 {
365 LOG_INFO() << "socket not (yet) connected, trying again.";
366 socket.connectToHost("localhost", 1314);
367 // appears we need to recheck the state still.
368 socket.waitForConnected();
369 }
370 else
371 {
372 // seems to be necessary to resend the request at times.
370 socket.write(QString("%1\n").arg(query).toLatin1()); 373 socket.write(QString("%1\n").arg(query).toLatin1());
371 socket.waitForBytesWritten(); 374 socket.waitForBytesWritten();
372 socket.waitForReadyRead(); 375 socket.waitForReadyRead();
373 376
374 response = socket.readAll().trimmed(); 377 // we might not get the complete response on the first read.
378 // Concatenate until we got a full response.
379 response += socket.readAll();
375 380
376 if (response != "LP" && response != "") 381 // The query response ends with this.
382 if (response.contains("ft_StUfF_keyOK"))
383 {
377 break; 384 break;
385 }
378 } 386 }
379 socket.abort();
380 socket.disconnectFromHost();
381 387
382 if(timeout > 0 && QDateTime::currentDateTime() >= endTime)
383 {
384 emit busyEnd();
385 return "";
386 }
387 /* make sure we wait a little as we don't want to flood the server 388 /* make sure we wait a little as we don't want to flood the server
388 * with requests */ 389 * with requests */
389 QDateTime tmpEndTime = QDateTime::currentDateTime().addMSecs(500); 390 QDateTime tmpEndTime = QDateTime::currentDateTime().addMSecs(500);
390 while(QDateTime::currentDateTime() < tmpEndTime) 391 while(QDateTime::currentDateTime() < tmpEndTime)
391 QCoreApplication::processEvents(QEventLoop::AllEvents); 392 QCoreApplication::processEvents(QEventLoop::AllEvents);
392 } 393 }
394 emit busyEnd();
395 socket.disconnectFromHost();
396
393 if(response == "nil") 397 if(response == "nil")
394 { 398 {
395 emit busyEnd();
396 return ""; 399 return "";
397 } 400 }
398 401
399 QStringList lines = response.split('\n'); 402 /* The response starts with "LP\n", and ends with "ft_StUfF_keyOK", but we
400 if(lines.size() > 2) 403 * could get trailing data -- we might have sent the request more than
404 * once. Use a regex to get the actual response part.
405 */
406 QRegularExpression regex("LP\\n(.*?)\\nft_StUfF_keyOK",
407 QRegularExpression::MultilineOption
408 | QRegularExpression::DotMatchesEverythingOption);
409 QRegularExpressionMatch match = regex.match(response);
410 if(match.hasMatch())
401 { 411 {
402 lines.removeFirst(); /* should be LP */ 412 response = match.captured(1);
403 lines.removeLast(); /* should be ft_StUfF_keyOK */ 413 }
414 else {
415 LOG_WARNING() << "Invalid Festival response." << response;
404 } 416 }
405 else
406 LOG_ERROR() << "Response too short:" << response;
407
408 emit busyEnd();
409 return lines.join("\n");
410 417
418 return response.trimmed();
411} 419}
412 420