summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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