diff options
Diffstat (limited to 'rbutil/rbutilqt/tts.cpp')
-rw-r--r-- | rbutil/rbutilqt/tts.cpp | 494 |
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() | |||
104 | TTSExes::TTSExes(QString name) : TTSBase() | 104 | TTSExes::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 | ||
114 | void TTSExes::setCfg(RbSettings* sett) | 114 | void 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 | ||
151 | bool TTSExes::start(QString *errStr) | 151 | bool 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 | ||
170 | TTSStatus TTSExes::voice(QString text,QString wavfile, QString *errStr) | 170 | TTSStatus 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() | |||
196 | bool TTSExes::configOk() | 196 | bool 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 | ||
217 | bool TTSSapi::start(QString *errStr) | 217 | bool 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) | |||
272 | QStringList TTSSapi::getVoiceList(QString language) | 272 | QStringList 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 | ||
322 | TTSStatus TTSSapi::voice(QString text,QString wavfile, QString *errStr) | 323 | TTSStatus 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 | ||
334 | bool TTSSapi::stop() | 335 | bool 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 | **********************************************************************/ |
371 | TTSFestival::~TTSFestival() | 373 | TTSFestival::~TTSFestival() |
372 | { | 374 | { |
373 | stop(); | 375 | stop(); |
374 | } | 376 | } |
375 | 377 | ||
376 | void TTSFestival::startServer() | 378 | void 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 | ||
393 | void TTSFestival::ensureServerRunning() | 395 | void 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 | ||
411 | bool TTSFestival::start(QString* errStr) | 413 | bool 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 | ||
421 | bool TTSFestival::stop() | 423 | bool 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 | ||
429 | TTSStatus TTSFestival::voice(QString text, QString wavfile, QString* errStr) | 431 | TTSStatus 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 | ||
461 | bool TTSFestival::configOk() | 463 | bool 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 | ||
473 | void TTSFestival::showCfg() | 475 | void TTSFestival::showCfg() |
@@ -481,138 +483,138 @@ void TTSFestival::showCfg() | |||
481 | 483 | ||
482 | QStringList TTSFestival::getVoiceList() | 484 | QStringList 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 | ||
510 | QString TTSFestival::getVoiceInfo(QString voice) | 512 | QString 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 | ||
557 | QString TTSFestival::queryServer(QString query, int timeout) | 559 | QString 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 | ||