summaryrefslogtreecommitdiff
path: root/rbutil
diff options
context:
space:
mode:
authorDominik Riebeling <Dominik.Riebeling@gmail.com>2012-01-14 16:17:13 +0100
committerDominik Riebeling <Dominik.Riebeling@gmail.com>2012-01-14 16:37:01 +0100
commit820dcfdfed9f7df52bf0242b3409258dfe4d558f (patch)
tree12fca4488157770a0b13052888d2019e2dbc08e3 /rbutil
parentc2f0ba7ecd81f89c534773ee371720321b341e80 (diff)
downloadrockbox-820dcfdfed9f7df52bf0242b3409258dfe4d558f.tar.gz
rockbox-820dcfdfed9f7df52bf0242b3409258dfe4d558f.zip
Remove multithreading support from voicefile creation.
Running TTS and encoders with multiple threads is causing problems on Windows since introduction of the feature (FS#12106, FS#11994). The current implementation also makes wrong assumptions (having multiple threads talk to the SAPI script doesn't make it run faster since it's still one thread responsible for creation). Completely remove multithreading support for that for now -- a different implementation is necessary. Change-Id: Icafa223644efc370a09186ce28ac83c22902e0c0
Diffstat (limited to 'rbutil')
-rw-r--r--rbutil/rbutilqt/base/talkgenerator.cpp247
-rw-r--r--rbutil/rbutilqt/base/talkgenerator.h31
2 files changed, 94 insertions, 184 deletions
diff --git a/rbutil/rbutilqt/base/talkgenerator.cpp b/rbutil/rbutilqt/base/talkgenerator.cpp
index 4e78872240..6dc0cebc19 100644
--- a/rbutil/rbutilqt/base/talkgenerator.cpp
+++ b/rbutil/rbutilqt/base/talkgenerator.cpp
@@ -21,16 +21,16 @@
21#include "systeminfo.h" 21#include "systeminfo.h"
22#include "wavtrim.h" 22#include "wavtrim.h"
23 23
24TalkGenerator::TalkGenerator(QObject* parent): QObject(parent), encFutureWatcher(this), ttsFutureWatcher(this) 24TalkGenerator::TalkGenerator(QObject* parent): QObject(parent)
25{ 25{
26 m_userAborted = false; 26
27 m_lang = "";
28} 27}
29 28
30//! \brief Creates Talkfiles. 29//! \brief Creates Talkfiles.
31//! 30//!
32TalkGenerator::Status TalkGenerator::process(QList<TalkEntry>* list,int wavtrimth) 31TalkGenerator::Status TalkGenerator::process(QList<TalkEntry>* list,int wavtrimth)
33{ 32{
33 m_abort = false;
34 QString errStr; 34 QString errStr;
35 bool warnings = false; 35 bool warnings = false;
36 36
@@ -103,122 +103,81 @@ TalkGenerator::Status TalkGenerator::process(QList<TalkEntry>* list,int wavtrimt
103//! 103//!
104TalkGenerator::Status TalkGenerator::voiceList(QList<TalkEntry>* list,int wavtrimth) 104TalkGenerator::Status TalkGenerator::voiceList(QList<TalkEntry>* list,int wavtrimth)
105{ 105{
106 emit logProgress(0, list->size()); 106 int progressMax = list->size();
107 int m_progress = 0;
108 emit logProgress(m_progress,progressMax);
107 109
110 QStringList errors;
108 QStringList duplicates; 111 QStringList duplicates;
109 112
110 m_ttsWarnings = false; 113 bool warnings = false;
111 for(int i=0; i < list->size(); i++) 114 for(int i=0; i < list->size(); i++)
112 { 115 {
113 (*list)[i].refs.tts = m_tts; 116 if(m_abort)
114 (*list)[i].refs.wavtrim = wavtrimth; 117 {
115 (*list)[i].refs.generator = this; 118 emit logItem(tr("Voicing aborted"), LOGERROR);
116 // enable voice corrections only if a language is set. 119 return eERROR;
117 if(!m_lang.isEmpty()) {
118 QString s = (*list)[i].toSpeak;
119 (*list)[i].toSpeak = correctString(s);
120 } 120 }
121 121
122 // skip duplicated wav entries 122 // skip duplicated wav entrys
123 if(!duplicates.contains(list->at(i).wavfilename)) 123 if(!duplicates.contains(list->at(i).wavfilename))
124 duplicates.append(list->at(i).wavfilename); 124 duplicates.append(list->at(i).wavfilename);
125 else 125 else
126 { 126 {
127 qDebug() << "[TalkGen] duplicate skipped"; 127 qDebug() << "[TalkGenerator] duplicate skipped";
128 (*list)[i].voiced = true; 128 (*list)[i].voiced = true;
129 emit logProgress(++m_progress,progressMax);
129 continue; 130 continue;
130 } 131 }
131 }
132
133 /* If the engine can't be parallelized, we use only 1 thread */
134 // NOTE: setting the number of maximum threads to use to 1 doesn't seem to
135 // work as expected -- it causes sporadically output files missing (see
136 // FS#11994). As a stop-gap solution use a separate implementation in that
137 // case for running the TTS.
138 if((m_tts->capabilities() & TTSBase::RunInParallel) != 0)
139 {
140 int maxThreadCount = QThreadPool::globalInstance()->maxThreadCount();
141 qDebug() << "[TalkGenerator] Maximum number of threads used:"
142 << QThreadPool::globalInstance()->maxThreadCount();
143
144 connect(&ttsFutureWatcher, SIGNAL(progressValueChanged(int)),
145 this, SLOT(ttsProgress(int)));
146 ttsFutureWatcher.setFuture(QtConcurrent::map(*list, &TalkGenerator::ttsEntryPoint));
147
148 /* We use this loop as an equivalent to ttsFutureWatcher.waitForFinished()
149 * since the latter blocks all events */
150 while(ttsFutureWatcher.isRunning())
151 QCoreApplication::processEvents();
152
153 /* Restore global settings, if we changed them */
154 if ((m_tts->capabilities() & TTSBase::RunInParallel) == 0)
155 QThreadPool::globalInstance()->setMaxThreadCount(maxThreadCount);
156 132
157 if(ttsFutureWatcher.isCanceled()) 133 // skip already voiced entrys
158 return eERROR; 134 if(list->at(i).voiced == true)
159 else if(m_ttsWarnings) 135 {
160 return eWARNING; 136 emit logProgress(++m_progress,progressMax);
161 else 137 continue;
162 return eOK; 138 }
163 } 139 // skip entry whith empty text
164 else { 140 if(list->at(i).toSpeak == "")
165 qDebug() << "[TalkGenerator] Using single thread TTS workaround"; 141 {
166 int items = list->size(); 142 emit logProgress(++m_progress,progressMax);
167 for(int i = 0; i < items; i++) { 143 continue;
168 if(m_userAborted) {
169 emit logItem(tr("Voicing aborted"), LOGERROR);
170 return eERROR;
171 }
172 TalkEntry entry = list->at(i);
173 TalkGenerator::ttsEntryPoint(entry);
174 (*list)[i] = entry;
175 emit logProgress(i, items);
176 } 144 }
177 return m_ttsWarnings ? eWARNING : eOK;
178 }
179}
180 145
181void TalkGenerator::ttsEntryPoint(TalkEntry& entry) 146 // voice entry
182{
183 if (!entry.voiced && !entry.toSpeak.isEmpty())
184 {
185 QString error; 147 QString error;
186 qDebug() << "[TalkGen] voicing: " << entry.toSpeak << "to" << entry.wavfilename; 148 qDebug() << "[TalkGenerator] voicing: " << list->at(i).toSpeak << "to" << list->at(i).wavfilename;
187 TTSStatus status = entry.refs.tts->voice(entry.toSpeak,entry.wavfilename, &error); 149 TTSStatus status = m_tts->voice(list->at(i).toSpeak,list->at(i).wavfilename, &error);
188 if (status == Warning || status == FatalError) 150 if(status == Warning)
189 { 151 {
190 entry.refs.generator->ttsFailEntry(entry, status, error); 152 warnings = true;
191 return; 153 emit logItem(tr("Voicing of %1 failed: %2").arg(list->at(i).toSpeak).arg(error),
154 LOGWARNING);
192 } 155 }
193 if (entry.refs.wavtrim != -1) 156 else if (status == FatalError)
157 {
158 emit logItem(tr("Voicing of %1 failed: %2").arg(list->at(i).toSpeak).arg(error),
159 LOGERROR);
160 return eERROR;
161 }
162 else
163 (*list)[i].voiced = true;
164
165 //wavetrim if needed
166 if(wavtrimth != -1)
194 { 167 {
195 char buffer[255]; 168 char buffer[255];
196 wavtrim(entry.wavfilename.toLocal8Bit().data(), entry.refs.wavtrim, buffer, 255); 169 wavtrim(list->at(i).wavfilename.toLocal8Bit().data(),wavtrimth,buffer,255);
197 } 170 }
198 entry.voiced = true;
199 }
200}
201 171
202void TalkGenerator::ttsFailEntry(const TalkEntry& entry, TTSStatus status, QString error) 172 emit logProgress(++m_progress,progressMax);
203{ 173 QCoreApplication::processEvents();
204 if(status == Warning)
205 {
206 m_ttsWarnings = true;
207 emit logItem(tr("Voicing of %1 failed: %2").arg(entry.toSpeak).arg(error),
208 LOGWARNING);
209 }
210 else if (status == FatalError)
211 {
212 emit logItem(tr("Voicing of %1 failed: %2").arg(entry.toSpeak).arg(error),
213 LOGERROR);
214 abort();
215 } 174 }
175 if(warnings)
176 return eWARNING;
177 else
178 return eOK;
216} 179}
217 180
218void TalkGenerator::ttsProgress(int value)
219{
220 emit logProgress(value,ttsFutureWatcher.progressMaximum());
221}
222 181
223//! \brief Encodes a List of strings 182//! \brief Encodes a List of strings
224//! 183//!
@@ -226,86 +185,58 @@ TalkGenerator::Status TalkGenerator::encodeList(QList<TalkEntry>* list)
226{ 185{
227 QStringList duplicates; 186 QStringList duplicates;
228 187
229 int itemsCount = list->size(); 188 int progressMax = list->size();
230 emit logProgress(0, itemsCount); 189 int m_progress = 0;
190 emit logProgress(m_progress,progressMax);
231 191
232 /* Do some preprocessing and remove entries that have not been voiced. */ 192 for(int i=0; i < list->size(); i++)
233 for (int idx=0; idx < itemsCount; idx++)
234 { 193 {
235 if(list->at(idx).voiced == false) 194 if(m_abort)
236 { 195 {
237 qDebug() << "[TalkGen] unvoiced entry" << list->at(idx).toSpeak <<"detected"; 196 emit logItem(tr("Encoding aborted"), LOGERROR);
238 list->removeAt(idx); 197 return eERROR;
239 itemsCount--; 198 }
240 idx--; 199
200 //skip non-voiced entrys
201 if(list->at(i).voiced == false)
202 {
203 qDebug() << "non voiced entry" << list->at(i).toSpeak <<"detected";
204 emit logProgress(++m_progress,progressMax);
241 continue; 205 continue;
242 } 206 }
243 if(duplicates.contains(list->at(idx).talkfilename)) 207 //skip duplicates
208 if(!duplicates.contains(list->at(i).talkfilename))
209 duplicates.append(list->at(i).talkfilename);
210 else
244 { 211 {
245 (*list)[idx].encoded = true; /* make sure we skip this entry */ 212 qDebug() << "[TalkGenerator] duplicate skipped";
213 (*list)[i].encoded = true;
214 emit logProgress(++m_progress,progressMax);
246 continue; 215 continue;
247 } 216 }
248 duplicates.append(list->at(idx).talkfilename);
249 (*list)[idx].refs.encoder = m_enc;
250 (*list)[idx].refs.generator = this; /* not really needed, unless we end up
251 voicing and encoding with two different
252 TalkGenerators.*/
253 }
254
255 connect(&encFutureWatcher, SIGNAL(progressValueChanged(int)),
256 this, SLOT(encProgress(int)));
257 encFutureWatcher.setFuture(QtConcurrent::map(*list, &TalkGenerator::encEntryPoint));
258
259 /* We use this loop as an equivalent to encFutureWatcher.waitForFinished()
260 * since the latter blocks all events */
261 while (encFutureWatcher.isRunning())
262 QCoreApplication::processEvents(QEventLoop::AllEvents);
263 217
264 if (encFutureWatcher.isCanceled()) 218 //encode entry
265 return eERROR; 219 qDebug() << "[TalkGenerator] encoding " << list->at(i).wavfilename
266 else 220 << "to" << list->at(i).talkfilename;
267 return eOK; 221 if(!m_enc->encode(list->at(i).wavfilename,list->at(i).talkfilename))
268} 222 {
269 223 emit logItem(tr("Encoding of %1 failed").arg(
270void TalkGenerator::encEntryPoint(TalkEntry& entry) 224 QFileInfo(list->at(i).wavfilename).baseName()), LOGERROR);
271{ 225 return eERROR;
272 if(!entry.encoded)
273 {
274 bool res = entry.refs.encoder->encode(entry.wavfilename, entry.talkfilename);
275 entry.encoded = res;
276 if (!entry.encoded)
277 entry.refs.generator->encFailEntry(entry);
278 } 226 }
279 return; 227 (*list)[i].encoded = true;
280} 228 emit logProgress(++m_progress,progressMax);
281 229 QCoreApplication::processEvents();
282void TalkGenerator::encProgress(int value) 230 }
283{ 231 return eOK;
284 emit logProgress(value, encFutureWatcher.progressMaximum());
285}
286
287void TalkGenerator::encFailEntry(const TalkEntry& entry)
288{
289 emit logItem(tr("Encoding of %1 failed").arg(
290 QFileInfo(entry.wavfilename).baseName()), LOGERROR);
291 abort();
292} 232}
293 233
294//! \brief slot, which is connected to the abort of the Logger. Sets a flag, so Creating Talkfiles ends at the next possible position 234//! \brief slot, which is connected to the abort of the Logger.
235//Sets a flag, so Creating Talkfiles ends at the next possible position
295//! 236//!
296void TalkGenerator::abort() 237void TalkGenerator::abort()
297{ 238{
298 if (ttsFutureWatcher.isRunning()) 239 m_abort = true;
299 {
300 ttsFutureWatcher.cancel();
301 emit logItem(tr("Voicing aborted"), LOGERROR);
302 }
303 if (encFutureWatcher.isRunning())
304 {
305 encFutureWatcher.cancel();
306 emit logItem(tr("Encoding aborted"), LOGERROR);
307 }
308 m_userAborted = true;
309} 240}
310 241
311QString TalkGenerator::correctString(QString s) 242QString TalkGenerator::correctString(QString s)
@@ -325,9 +256,9 @@ QString TalkGenerator::correctString(QString s)
325 qDebug() << "[VoiceFileCreator] corrected string" << s << "to" << corrected; 256 qDebug() << "[VoiceFileCreator] corrected string" << s << "to" << corrected;
326 257
327 return corrected; 258 return corrected;
259 m_abort = true;
328} 260}
329 261
330
331void TalkGenerator::setLang(QString name) 262void TalkGenerator::setLang(QString name)
332{ 263{
333 m_lang = name; 264 m_lang = name;
diff --git a/rbutil/rbutilqt/base/talkgenerator.h b/rbutil/rbutilqt/base/talkgenerator.h
index 5fe036e2f1..3e2f9394fb 100644
--- a/rbutil/rbutilqt/base/talkgenerator.h
+++ b/rbutil/rbutilqt/base/talkgenerator.h
@@ -48,30 +48,15 @@ public:
48 QString target; 48 QString target;
49 bool voiced; 49 bool voiced;
50 bool encoded; 50 bool encoded;
51
52 /* We need the following members because
53 * 1) the QtConcurrent entry points are all static methods (and we
54 * need to communicate with the TalkGenerator)
55 * 2) we are not guaranteed to go through the list in any
56 * particular order, so we can't use the progress slot
57 * for error checking */
58 struct
59 {
60 EncoderBase* encoder;
61 TTSBase* tts;
62 TalkGenerator* generator;
63 int wavtrim;
64 } refs;
65 }; 51 };
66 52
67 TalkGenerator(QObject* parent); 53 TalkGenerator(QObject* parent);
54
68 Status process(QList<TalkEntry>* list,int wavtrimth = -1); 55 Status process(QList<TalkEntry>* list,int wavtrimth = -1);
69 QString correctString(QString s); 56 QString correctString(QString s);
70 57
71public slots: 58public slots:
72 void abort(); 59 void abort();
73 void encProgress(int value);
74 void ttsProgress(int value);
75 void setLang(QString name); 60 void setLang(QString name);
76 61
77signals: 62signals:
@@ -80,22 +65,12 @@ signals:
80 void logProgress(int, int); //! set progress bar. 65 void logProgress(int, int); //! set progress bar.
81 66
82private: 67private:
83 QFutureWatcher<void> encFutureWatcher;
84 QFutureWatcher<void> ttsFutureWatcher;
85 void encFailEntry(const TalkEntry& entry);
86 void ttsFailEntry(const TalkEntry& entry, TTSStatus status, QString error);
87
88 Status voiceList(QList<TalkEntry>* list,int wavetrimth); 68 Status voiceList(QList<TalkEntry>* list,int wavetrimth);
89 Status encodeList(QList<TalkEntry>* list); 69 Status encodeList(QList<TalkEntry>* list);
90 70
91 static void encEntryPoint(TalkEntry& entry);
92 static void ttsEntryPoint(TalkEntry& entry);
93
94 TTSBase* m_tts; 71 TTSBase* m_tts;
95 EncoderBase* m_enc; 72 EncoderBase* m_enc;
96 73
97 bool m_ttsWarnings;
98 bool m_userAborted;
99 QString m_lang; 74 QString m_lang;
100 75
101 struct CorrectionItems 76 struct CorrectionItems
@@ -105,6 +80,10 @@ private:
105 QString modifier; 80 QString modifier;
106 }; 81 };
107 QList<struct CorrectionItems> m_corrections; 82 QList<struct CorrectionItems> m_corrections;
83
84 bool m_abort;
85
86
108}; 87};
109 88
110 89