diff options
-rw-r--r-- | utils/rbutilqt/base/ttsfestival.cpp | 88 |
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 | ||
333 | QString TTSFestival::queryServer(QString query, int timeout) | 330 | QString 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 | ||