summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDominik Wenger <domonoky@googlemail.com>2010-03-30 17:45:23 +0000
committerDominik Wenger <domonoky@googlemail.com>2010-03-30 17:45:23 +0000
commit11c9be9c831399508b3d0128290349bc71c97520 (patch)
treed7d7bbeeb6a59c377aae2bdffdea6207792201ef
parenta79fee019e3901edb8c7f3a2b5ba208a57c06a00 (diff)
downloadrockbox-11c9be9c831399508b3d0128290349bc71c97520.tar.gz
rockbox-11c9be9c831399508b3d0128290349bc71c97520.zip
Fix Festival tts engine.
Author: Delyan Kratunov Flyspray: FS#11155 part2 git-svn-id: svn://svn.rockbox.org/rockbox/trunk@25402 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--rbutil/rbutilqt/base/ttsfestival.cpp154
-rw-r--r--rbutil/rbutilqt/base/ttsfestival.h14
2 files changed, 110 insertions, 58 deletions
diff --git a/rbutil/rbutilqt/base/ttsfestival.cpp b/rbutil/rbutilqt/base/ttsfestival.cpp
index 06cf0ef0cc..be67b67bd2 100644
--- a/rbutil/rbutilqt/base/ttsfestival.cpp
+++ b/rbutil/rbutilqt/base/ttsfestival.cpp
@@ -23,6 +23,7 @@
23 23
24TTSFestival::~TTSFestival() 24TTSFestival::~TTSFestival()
25{ 25{
26 qDebug() << "[Festival] Destroying instance";
26 stop(); 27 stop();
27} 28}
28 29
@@ -48,7 +49,7 @@ void TTSFestival::generateSettings()
48 EncTtsSetting* setting = new EncTtsSetting(this, 49 EncTtsSetting* setting = new EncTtsSetting(this,
49 EncTtsSetting::eSTRINGLIST, tr("Voice:"), 50 EncTtsSetting::eSTRINGLIST, tr("Voice:"),
50 RbSettings::subValue("festival", RbSettings::TtsVoice), 51 RbSettings::subValue("festival", RbSettings::TtsVoice),
51 getVoiceList(exepath), EncTtsSetting::eREFRESHBTN); 52 getVoiceList(), EncTtsSetting::eREFRESHBTN);
52 connect(setting,SIGNAL(refresh()),this,SLOT(updateVoiceList())); 53 connect(setting,SIGNAL(refresh()),this,SLOT(updateVoiceList()));
53 connect(setting,SIGNAL(dataChanged()),this,SLOT(clearVoiceDescription())); 54 connect(setting,SIGNAL(dataChanged()),this,SLOT(clearVoiceDescription()));
54 insertSetting(eVOICE,setting); 55 insertSetting(eVOICE,setting);
@@ -76,8 +77,10 @@ void TTSFestival::saveSettings()
76void TTSFestival::updateVoiceDescription() 77void TTSFestival::updateVoiceDescription()
77{ 78{
78 // get voice Info with current voice and path 79 // get voice Info with current voice and path
79 QString info = getVoiceInfo(getSetting(eVOICE)->current().toString(), 80 currentPath = getSetting(eSERVERPATH)->current().toString();
80 getSetting(eSERVERPATH)->current().toString()); 81 QString info = getVoiceInfo(getSetting(eVOICE)->current().toString());
82 currentPath = "";
83
81 getSetting(eVOICEDESC)->setCurrent(info); 84 getSetting(eVOICEDESC)->setCurrent(info);
82} 85}
83 86
@@ -88,47 +91,78 @@ void TTSFestival::clearVoiceDescription()
88 91
89void TTSFestival::updateVoiceList() 92void TTSFestival::updateVoiceList()
90{ 93{
91 QStringList voiceList = getVoiceList(getSetting(eSERVERPATH)->current().toString()); 94 currentPath = getSetting(eSERVERPATH)->current().toString();
95 QStringList voiceList = getVoiceList();
96 currentPath = "";
97
92 getSetting(eVOICE)->setList(voiceList); 98 getSetting(eVOICE)->setList(voiceList);
93 if(voiceList.size() > 0) getSetting(eVOICE)->setCurrent(voiceList.at(0)); 99 if(voiceList.size() > 0) getSetting(eVOICE)->setCurrent(voiceList.at(0));
94 else getSetting(eVOICE)->setCurrent(""); 100 else getSetting(eVOICE)->setCurrent("");
95} 101}
96 102
97void TTSFestival::startServer(QString path) 103void TTSFestival::startServer()
98{ 104{
99 if(!configOk()) 105 if(!configOk())
100 return; 106 return;
101 107
102 if(path == "") 108 if(serverProcess.state() != QProcess::Running)
103 path = RbSettings::subValue("festival-server",RbSettings::TtsPath).toString(); 109 {
104 110 QString path;
105 serverProcess.start(QString("%1 --server").arg(path)); 111 /* currentPath is set by the GUI - if it's set, it is the currently set
106 serverProcess.waitForStarted(); 112 path in the configuration GUI; if it's not set, use the saved path */
107 113 if (currentPath == "")
108 queryServer("(getpid)",300,path); 114 path = RbSettings::subValue("festival-server",RbSettings::TtsPath).toString();
109 if(serverProcess.state() == QProcess::Running) 115 else
110 qDebug() << "Festival is up and running"; 116 path = currentPath;
111 else 117
112 qDebug() << "Festival failed to start"; 118 serverProcess.start(QString("%1 --server").arg(path));
119 serverProcess.waitForStarted();
120
121 /* A friendlier version of a spinlock */
122 while (serverProcess.pid() == 0 && serverProcess.state() != QProcess::Running)
123 QCoreApplication::processEvents(QEventLoop::AllEvents, 50);
124
125 if(serverProcess.state() == QProcess::Running)
126 qDebug() << "[Festival] Server is up and running";
127 else
128 qDebug() << "[Festival] Server failed to start, state: " << serverProcess.state();
129 }
113} 130}
114 131
115void TTSFestival::ensureServerRunning(QString path) 132bool TTSFestival::ensureServerRunning()
116{ 133{
117 if(serverProcess.state() != QProcess::Running) 134 if(serverProcess.state() != QProcess::Running)
118 { 135 {
119 startServer(path); 136 startServer();
120 } 137 }
138 return serverProcess.state() == QProcess::Running;
121} 139}
122 140
123bool TTSFestival::start(QString* errStr) 141bool TTSFestival::start(QString* errStr)
124{ 142{
125 (void) errStr; 143 qDebug() << "[Festival] Starting server with voice " << RbSettings::subValue("festival", RbSettings::TtsVoice).toString();
126 ensureServerRunning(); 144
145 bool running = ensureServerRunning();
127 if (!RbSettings::subValue("festival",RbSettings::TtsVoice).toString().isEmpty()) 146 if (!RbSettings::subValue("festival",RbSettings::TtsVoice).toString().isEmpty())
128 queryServer(QString("(voice.select '%1)") 147 {
129 .arg(RbSettings::subValue("festival", RbSettings::TtsVoice).toString())); 148 /* There's no harm in using both methods to set the voice .. */
130 149 QString voiceSelect = QString("(voice.select '%1)\n")
131 return true; 150 .arg(RbSettings::subValue("festival", RbSettings::TtsVoice).toString());
151 queryServer(voiceSelect, 3000);
152
153 if(prologFile.open())
154 {
155 prologFile.write(voiceSelect.toAscii());
156 prologFile.close();
157 prologPath = QFileInfo(prologFile).absoluteFilePath();
158 qDebug() << "[Festival] Prolog created at " << prologPath;
159 }
160
161 }
162
163 if (!running)
164 (*errStr) = "Festival could not be started";
165 return running;
132} 166}
133 167
134bool TTSFestival::stop() 168bool TTSFestival::stop()
@@ -141,13 +175,13 @@ bool TTSFestival::stop()
141 175
142TTSStatus TTSFestival::voice(QString text, QString wavfile, QString* errStr) 176TTSStatus TTSFestival::voice(QString text, QString wavfile, QString* errStr)
143{ 177{
144 qDebug() << text << "->" << wavfile; 178 qDebug() << "[Festival] Voicing " << text << "->" << wavfile;
145 179
146 QString path = RbSettings::subValue("festival-client", 180 QString path = RbSettings::subValue("festival-client",
147 RbSettings::TtsPath).toString(); 181 RbSettings::TtsPath).toString();
148 QString cmd = QString("%1 --server localhost --otype riff --ttw --withlisp" 182 QString cmd = QString("%1 --server localhost --otype riff --ttw --withlisp"
149 " --output \"%2\" - ").arg(path).arg(wavfile); 183 " --output \"%2\" --prolog \"%3\" - ").arg(path).arg(wavfile).arg(prologPath);
150 qDebug() << cmd; 184 qDebug() << "[Festival] Client cmd: " << cmd;
151 185
152 QProcess clientProcess; 186 QProcess clientProcess;
153 clientProcess.start(cmd); 187 clientProcess.start(cmd);
@@ -159,7 +193,7 @@ TTSStatus TTSFestival::voice(QString text, QString wavfile, QString* errStr)
159 response = response.trimmed(); 193 response = response.trimmed();
160 if(!response.contains("Utterance")) 194 if(!response.contains("Utterance"))
161 { 195 {
162 qDebug() << "Could not voice string: " << response; 196 qDebug() << "[Festival] Could not voice string: " << response;
163 *errStr = tr("engine could not voice string"); 197 *errStr = tr("engine could not voice string");
164 return Warning; 198 return Warning;
165 /* do not stop the voicing process because of a single string 199 /* do not stop the voicing process because of a single string
@@ -175,32 +209,40 @@ TTSStatus TTSFestival::voice(QString text, QString wavfile, QString* errStr)
175 209
176bool TTSFestival::configOk() 210bool TTSFestival::configOk()
177{ 211{
178 QString serverPath = RbSettings::subValue("festival-server", 212 bool ret;
179 RbSettings::TtsPath).toString(); 213 if (currentPath == "")
180 QString clientPath = RbSettings::subValue("festival-client", 214 {
181 RbSettings::TtsPath).toString(); 215 QString serverPath = RbSettings::subValue("festival-server",
182 216 RbSettings::TtsPath).toString();
183 bool ret = QFileInfo(serverPath).isExecutable() && 217 QString clientPath = RbSettings::subValue("festival-client",
184 QFileInfo(clientPath).isExecutable(); 218 RbSettings::TtsPath).toString();
185 if(RbSettings::subValue("festival",RbSettings::TtsVoice).toString().size() > 0 219
186 && voices.size() > 0) 220 ret = QFileInfo(serverPath).isExecutable() &&
187 ret = ret && (voices.indexOf(RbSettings::subValue("festival", 221 QFileInfo(clientPath).isExecutable();
188 RbSettings::TtsVoice).toString()) != -1); 222 if(RbSettings::subValue("festival",RbSettings::TtsVoice).toString().size() > 0
223 && voices.size() > 0)
224 ret = ret && (voices.indexOf(RbSettings::subValue("festival",
225 RbSettings::TtsVoice).toString()) != -1);
226 }
227 else /* If we're currently configuring the server, we need to know that
228 the entered path is valid */
229 ret = QFileInfo(currentPath).isExecutable();
230
189 return ret; 231 return ret;
190} 232}
191 233
192QStringList TTSFestival::getVoiceList(QString path) 234QStringList TTSFestival::getVoiceList()
193{ 235{
194 if(!configOk()) 236 if(!configOk())
195 return QStringList(); 237 return QStringList();
196 238
197 if(voices.size() > 0) 239 if(voices.size() > 0)
198 { 240 {
199 qDebug() << "Using voice cache"; 241 qDebug() << "[Festival] Using voice cache";
200 return voices; 242 return voices;
201 } 243 }
202 244
203 QString response = queryServer("(voice.list)",3000,path); 245 QString response = queryServer("(voice.list)", 10000);
204 246
205 // get the 2nd line. It should be (<voice_name>, <voice_name>) 247 // get the 2nd line. It should be (<voice_name>, <voice_name>)
206 response = response.mid(response.indexOf('\n') + 1, -1); 248 response = response.mid(response.indexOf('\n') + 1, -1);
@@ -212,14 +254,14 @@ QStringList TTSFestival::getVoiceList(QString path)
212 if (voices.size() == 1 && voices[0].size() == 0) 254 if (voices.size() == 1 && voices[0].size() == 0)
213 voices.removeAt(0); 255 voices.removeAt(0);
214 if (voices.size() > 0) 256 if (voices.size() > 0)
215 qDebug() << "Voices: " << voices; 257 qDebug() << "[Festival] Voices: " << voices;
216 else 258 else
217 qDebug() << "No voices."; 259 qDebug() << "[Festival] No voices. Response was: " << response;
218 260
219 return voices; 261 return voices;
220} 262}
221 263
222QString TTSFestival::getVoiceInfo(QString voice,QString path) 264QString TTSFestival::getVoiceInfo(QString voice)
223{ 265{
224 if(!configOk()) 266 if(!configOk())
225 return ""; 267 return "";
@@ -231,7 +273,7 @@ QString TTSFestival::getVoiceInfo(QString voice,QString path)
231 return voiceDescriptions[voice]; 273 return voiceDescriptions[voice];
232 274
233 QString response = queryServer(QString("(voice.description '%1)").arg(voice), 275 QString response = queryServer(QString("(voice.description '%1)").arg(voice),
234 3000,path); 276 10000);
235 277
236 if (response == "") 278 if (response == "")
237 { 279 {
@@ -241,7 +283,7 @@ QString TTSFestival::getVoiceInfo(QString voice,QString path)
241 { 283 {
242 response = response.remove(QRegExp("(description \"*\")", 284 response = response.remove(QRegExp("(description \"*\")",
243 Qt::CaseInsensitive, QRegExp::Wildcard)); 285 Qt::CaseInsensitive, QRegExp::Wildcard));
244 qDebug() << "voiceInfo w/o descr: " << response; 286 qDebug() << "[Festival] voiceInfo w/o descr: " << response;
245 response = response.remove(')'); 287 response = response.remove(')');
246 QStringList responseLines = response.split('(', QString::SkipEmptyParts); 288 QStringList responseLines = response.split('(', QString::SkipEmptyParts);
247 responseLines.removeAt(0); // the voice name itself 289 responseLines.removeAt(0); // the voice name itself
@@ -271,17 +313,23 @@ QString TTSFestival::getVoiceInfo(QString voice,QString path)
271 return voiceDescriptions[voice]; 313 return voiceDescriptions[voice];
272} 314}
273 315
274QString TTSFestival::queryServer(QString query, int timeout,QString path) 316QString TTSFestival::queryServer(QString query, int timeout)
275{ 317{
276 if(!configOk()) 318 if(!configOk())
277 return ""; 319 return "";
278 320
279 // this operation could take some time 321 // this operation could take some time
280 emit busy(); 322 emit busy();
323
324 qDebug() << "[Festival] queryServer with " << query;
281 325
282 ensureServerRunning(path); 326 if (!ensureServerRunning())
327 {
328 qDebug() << "[Festival] queryServer: ensureServerRunning failed";
329 emit busyEnd();
330 return "";
331 }
283 332
284 qDebug() << "queryServer with " << query;
285 QString response; 333 QString response;
286 334
287 QDateTime endTime; 335 QDateTime endTime;
@@ -334,11 +382,11 @@ QString TTSFestival::queryServer(QString query, int timeout,QString path)
334 QStringList lines = response.split('\n'); 382 QStringList lines = response.split('\n');
335 if(lines.size() > 2) 383 if(lines.size() > 2)
336 { 384 {
337 lines.removeFirst(); 385 lines.removeFirst(); /* should be LP */
338 lines.removeLast(); 386 lines.removeLast(); /* should be ft_StUfF_keyOK */
339 } 387 }
340 else 388 else
341 qDebug() << "Response too short: " << response; 389 qDebug() << "[Festival] Response too short: " << response;
342 390
343 emit busyEnd(); 391 emit busyEnd();
344 return lines.join("\n"); 392 return lines.join("\n");
diff --git a/rbutil/rbutilqt/base/ttsfestival.h b/rbutil/rbutilqt/base/ttsfestival.h
index 00a086af15..8a687375bc 100644
--- a/rbutil/rbutilqt/base/ttsfestival.h
+++ b/rbutil/rbutilqt/base/ttsfestival.h
@@ -22,6 +22,7 @@
22#ifndef TTSFESTIVAL_H 22#ifndef TTSFESTIVAL_H
23#define TTSFESTIVAL_H 23#define TTSFESTIVAL_H
24 24
25#include <QTemporaryFile>
25#include "ttsbase.h" 26#include "ttsbase.h"
26 27
27class TTSFestival : public TTSBase 28class TTSFestival : public TTSBase
@@ -52,12 +53,15 @@ class TTSFestival : public TTSBase
52 void updateVoiceDescription(); 53 void updateVoiceDescription();
53 void clearVoiceDescription(); 54 void clearVoiceDescription();
54 private: 55 private:
55 QStringList getVoiceList(QString path =""); 56 QTemporaryFile prologFile;
56 QString getVoiceInfo(QString voice,QString path =""); 57 QString prologPath;
58 QString currentPath;
59 QStringList getVoiceList();
60 QString getVoiceInfo(QString voice);
57 61
58 inline void startServer(QString path=""); 62 inline void startServer();
59 inline void ensureServerRunning(QString path=""); 63 inline bool ensureServerRunning();
60 QString queryServer(QString query, int timeout = -1,QString path=""); 64 QString queryServer(QString query, int timeout = -1);
61 QProcess serverProcess; 65 QProcess serverProcess;
62 QStringList voices; 66 QStringList voices;
63 QMap<QString, QString> voiceDescriptions; 67 QMap<QString, QString> voiceDescriptions;