SVN_SILENT made messages (.desktop file)
[kdeaccessibility.git] / kttsd / plugins / festivalint / festivalintconf.cpp
blob64a054749ebe970c95743c909e705928235e7ef4
1 /***************************************************** vim:set ts=4 sw=4 sts=4:
2 Configuration widget and functions for Festival (Interactive) plug in
3 -------------------
4 Copyright:
5 (C) 2004 by Gary Cramblitt <garycramblitt@comcast.net>
6 -------------------
7 Original author: Gary Cramblitt <garycramblitt@comcast.net>
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 ******************************************************************************/
24 // FestivalInt includes.
25 #include "festivalintconf.h"
26 #include "festivalintconf.moc"
27 #include "festivalintproc.h"
29 // C++ includes.
30 #include <math.h>
32 // Qt includes.
33 #include <QtGui/QLabel>
34 #include <QtGui/QCheckBox>
35 #include <QtCore/QDir>
36 #include <QtGui/QSlider>
37 #include <QtXml/qdom.h>
38 #include <QtCore/QTextCodec>
39 #include <QtGui/QPixmap>
41 // KDE includes.
42 #include <kdebug.h>
43 #include <klocale.h>
44 #include <kcombobox.h>
45 #include <kglobal.h>
46 #include <ktemporaryfile.h>
47 #include <kstandarddirs.h>
48 #include <knuminput.h>
49 #include <k3procio.h>
50 #include <kprogressdialog.h>
51 #include <kicon.h>
53 // KTTS includes.
54 #include "testplayer.h"
56 /** Constructor */
57 FestivalIntConf::FestivalIntConf( QWidget* parent, const QStringList& /*args*/) :
58 PlugInConf(parent, "festivalintconf")
60 // kDebug() << "FestivalIntConf::FestivalIntConf: Running";
61 m_festProc = 0;
62 m_progressDlg = 0;
63 m_supportsSSML = FestivalIntProc::ssUnknown;
65 setupUi(this);
67 festivalPath->setMode(KFile::File | KFile::ExistingOnly);
68 festivalPath->setFilter("*");
70 // Build codec list and fill combobox.
71 m_codecList = PlugInProc::buildCodecList();
72 characterCodingBox->clear();
73 characterCodingBox->addItems(m_codecList);
75 // defaults();
77 connect(festivalPath, SIGNAL(textChanged(const QString&)),
78 this, SLOT(slotFestivalPath_textChanged()));
79 connect(selectVoiceCombo, SIGNAL(activated(const QString&)),
80 this, SLOT(slotSelectVoiceCombo_activated()));
81 connect(selectVoiceCombo, SIGNAL(activated(const QString&)),
82 this, SLOT(configChanged()));
83 connect(testButton, SIGNAL(clicked()), this, SLOT(slotTest_clicked()));
84 connect(rescan, SIGNAL(clicked()), this, SLOT(scanVoices()));
85 connect(volumeBox, SIGNAL(valueChanged(int)),
86 this, SLOT(volumeBox_valueChanged(int)));
87 connect(timeBox, SIGNAL(valueChanged(int)),
88 this, SLOT(timeBox_valueChanged(int)));
89 connect(frequencyBox, SIGNAL(valueChanged(int)),
90 this, SLOT(frequencyBox_valueChanged(int)));
91 connect(volumeSlider, SIGNAL(valueChanged(int)),
92 this, SLOT(volumeSlider_valueChanged(int)));
93 connect(timeSlider, SIGNAL(valueChanged(int)),
94 this, SLOT(timeSlider_valueChanged(int)));
95 connect(frequencySlider, SIGNAL(valueChanged(int)),
96 this, SLOT(frequencySlider_valueChanged(int)));
97 connect(volumeBox, SIGNAL(valueChanged(int)), this, SLOT(configChanged()));
98 connect(volumeSlider, SIGNAL(valueChanged(int)), this, SLOT(configChanged()));
99 connect(timeBox, SIGNAL(valueChanged(int)), this, SLOT(configChanged()));
100 connect(timeSlider, SIGNAL(valueChanged(int)), this, SLOT(configChanged()));
101 connect(frequencyBox, SIGNAL(valueChanged(int)), this, SLOT(configChanged()));
102 connect(frequencySlider, SIGNAL(valueChanged(int)), this, SLOT(configChanged()));
103 connect(preloadCheckBox, SIGNAL(clicked()), this, SLOT(configChanged()));
104 connect(characterCodingBox, SIGNAL(textChanged(const QString&)),
105 this, SLOT(configChanged()));
106 connect(characterCodingBox, SIGNAL(activated(const QString&)),
107 this, SLOT(configChanged()));
110 /** Destructor */
111 FestivalIntConf::~FestivalIntConf(){
112 // kDebug() << "FestivalIntConf::~FestivalIntConf: Running";
113 if (!m_waveFile.isNull()) QFile::remove(m_waveFile);
114 delete m_festProc;
115 delete m_progressDlg;
119 * Given a voice code, returns index into m_voiceList array (and voiceCombo box).
120 * -1 if not found.
122 int FestivalIntConf::voiceCodeToListIndex(const QString& voiceCode) const
124 for (int i = 0; i < m_voiceList.size(); ++i)
125 if (m_voiceList[i].code == voiceCode) return i;
126 return -1;
129 void FestivalIntConf::load(KConfig *c, const QString &configGroup){
130 //kDebug() << "FestivalIntConf::load: Running";
131 KConfigGroup festivalConfig(c, "FestivalInt");
132 QString exePath = festivalConfig.readEntry("FestivalExecutablePath", "festival");
133 QString exeLocation = getLocation(exePath);
134 if (!exeLocation.isEmpty()) exePath = exeLocation;
135 exePath = realFilePath(exePath);
137 KConfigGroup config(c, configGroup);
138 festivalPath->setUrl(KUrl::fromPath(config.readEntry("FestivalExecutablePath", exePath)));
139 preloadCheckBox->setChecked(false);
140 scanVoices();
141 QString voiceSelected(config.readEntry("Voice"));
142 int index = voiceCodeToListIndex(voiceSelected);
143 if (index >= 0)
145 selectVoiceCombo->setCurrentIndex(index);
146 preloadCheckBox->setChecked(m_voiceList[index].preload);
148 volumeBox->setValue(config.readEntry("volume", 100));
149 timeBox->setValue(config.readEntry("time", 100));
150 frequencyBox->setValue(config.readEntry("pitch", 100));
151 preloadCheckBox->setChecked(config.readEntry(
152 "Preload", preloadCheckBox->isChecked()));
153 m_languageCode = config.readEntry("LanguageCode", m_languageCode);
154 m_supportsSSML = static_cast<FestivalIntProc::SupportsSSML>(
155 config.readEntry("SupportsSSML", int(FestivalIntProc::ssUnknown)));
156 QString codecName = PlugInProc::codecIndexToCodecName(
157 characterCodingBox->currentIndex(), m_codecList);
158 codecName = config.readEntry("Codec", codecName);
159 int codecNdx = PlugInProc::codecNameToListIndex(codecName, m_codecList);
160 characterCodingBox->setCurrentIndex(codecNdx);
163 void FestivalIntConf::save(KConfig *c, const QString &configGroup){
164 // kDebug() << "FestivalIntConf::save: Running";
165 KConfigGroup festivalConfig(c, "FestivalInt");
166 festivalConfig.writeEntry("FestivalExecutablePath", realFilePath(festivalPath->url().path()));
168 KConfigGroup config(c, configGroup);
169 config.writeEntry("FestivalExecutablePath", realFilePath(festivalPath->url().path()));
170 config.writeEntry("Voice", m_voiceList[selectVoiceCombo->currentIndex()].code);
171 config.writeEntry("volume", volumeBox->value());
172 config.writeEntry("time", timeBox->value());
173 config.writeEntry("pitch", frequencyBox->value());
174 config.writeEntry("Preload", preloadCheckBox->isChecked());
175 config.writeEntry("LanguageCode", m_voiceList[selectVoiceCombo->currentIndex()].languageCode);
176 config.writeEntry("SupportsSSML", int(m_supportsSSML));
177 int codec = characterCodingBox->currentIndex();
178 config.writeEntry("Codec", PlugInProc::codecIndexToCodecName(codec, m_codecList));
181 void FestivalIntConf::defaults(){
182 // kDebug() << "FestivalIntConf::defaults: Running";
183 festivalPath->setUrl(KUrl("festival"));
184 timeBox->setValue(100);
185 timeBox_valueChanged(100);
186 volumeBox->setValue(100);
187 volumeBox_valueChanged(100);
188 frequencyBox->setValue(100);
189 frequencyBox_valueChanged(100);
190 preloadCheckBox->setChecked(false);
191 characterCodingBox->setCurrentIndex(
192 PlugInProc::codecNameToListIndex("ISO 8859-1", m_codecList));
193 scanVoices();
196 void FestivalIntConf::setDesiredLanguage(const QString &lang)
198 // kDebug() << "FestivalIntConf::setDesiredLanguage: Running";
199 m_languageCode = splitLanguageCode(lang, m_countryCode);
202 QString FestivalIntConf::getTalkerCode()
204 if (!selectVoiceCombo->isEnabled()) return QString();
205 QString exePath = realFilePath(festivalPath->url().path());
206 if (exePath.isEmpty()) return QString();
207 if (getLocation(exePath).isEmpty()) return QString();
208 if (m_voiceList.count() == 0) return QString();
209 QString normalTalkerCode;
210 voiceStruct voiceTemp = m_voiceList[selectVoiceCombo->currentIndex()];
211 // Determine volume attribute. soft < 75% <= medium <= 125% < loud.
212 QString volume = "medium";
213 if (volumeBox->value() < 75) volume = "soft";
214 if (volumeBox->value() > 125) volume = "loud";
215 // Determine rate attribute. slow < 75% <= medium <= 125% < fast.
216 QString rate = "medium";
217 if (timeBox->value() < 75) rate = "slow";
218 if (timeBox->value() > 125) rate = "fast";
219 normalTalkerCode = QString(
220 "<voice lang=\"%1\" name=\"%2\" gender=\"%3\" />"
221 "<prosody volume=\"%4\" rate=\"%5\" />"
222 "<kttsd synthesizer=\"%6\" />")
223 .arg(voiceTemp.languageCode)
224 .arg(voiceTemp.code)
225 .arg(voiceTemp.gender)
226 .arg(volume)
227 .arg(rate)
228 .arg("Festival Interactive");
229 return normalTalkerCode;
233 * Chooses a default voice given scanned list of voices in m_voiceList and current
234 * language and country code, and updates controls.
235 * @param currentVoiceIndex This voice is preferred if it matches.
237 void FestivalIntConf::setDefaultVoice(int currentVoiceIndex)
239 // kDebug() << "FestivalIntCont::setDefaultVoice: Running";
240 // If language code is known, auto pick first voice that matches the language code.
241 if (!m_languageCode.isEmpty())
243 bool found = false;
244 // First search for a match on both language code and country code.
245 QString languageCode = m_languageCode;
246 if (!m_countryCode.isNull()) languageCode += '_' + m_countryCode;
247 // kDebug() << "FestivalIntConf::setDefaultVoice:: looking for default voice to match language code " << languageCode;
248 int index = 0;
249 // Prefer existing voice if it matches.
250 if (currentVoiceIndex >= 0)
252 QString vlCode = m_voiceList[currentVoiceIndex].languageCode.left(languageCode.length());
253 if (languageCode == vlCode)
255 found = true;
256 index = currentVoiceIndex;
259 if (!found)
261 for(index = 0 ; index < m_voiceList.count(); ++index)
263 QString vlCode = m_voiceList[index].languageCode.left(languageCode.length());
264 // kDebug() << "FestivalIntConf::setDefaultVoice: testing " << vlCode;
265 if(languageCode == vlCode)
267 found = true;
268 break;
272 // If not found, search for a match on just the language code.
273 if (!found)
275 languageCode = m_languageCode;
276 // Prefer existing voice if it matches.
277 if (currentVoiceIndex >= 0)
279 QString vlCode = m_voiceList[currentVoiceIndex].languageCode.left(languageCode.length());
280 if (languageCode == vlCode)
282 found = true;
283 index = currentVoiceIndex;
286 if (!found)
288 for(index = 0 ; index < m_voiceList.count(); ++index)
290 QString vlCode = m_voiceList[index].languageCode.left(languageCode.length());
291 // kDebug() << "FestivalIntConf::setDefaultVoice: testing " << vlCode;
292 if(languageCode == vlCode)
294 found = true;
295 break;
300 // If not found, pick first voice that is not "Unknown".
301 if (!found)
303 for(index = 0 ; index < m_voiceList.count(); ++index)
305 if (m_voiceList[index].name != i18n("Unknown"))
307 found = true;
308 break;
312 if (found)
314 // kDebug() << "FestivalIntConf::setDefaultVoice: auto picking voice code " << m_voiceList[index].code;
315 selectVoiceCombo->setCurrentIndex(index);
316 preloadCheckBox->setChecked(m_voiceList[index].preload);
317 QString codecName = m_voiceList[index].codecName;
318 int codecNdx = PlugInProc::codecNameToListIndex(codecName, m_codecList);
319 characterCodingBox->setCurrentIndex(codecNdx);
320 if (m_voiceList[index].volumeAdjustable)
322 volumeBox->setEnabled(true);
323 volumeSlider->setEnabled(true);
325 else
327 volumeBox->setValue(100);
328 volumeBox_valueChanged(100);
329 volumeBox->setEnabled(false);
330 volumeSlider->setEnabled(false);
332 if (m_voiceList[index].rateAdjustable)
334 timeBox->setEnabled(true);
335 timeSlider->setEnabled(true);
337 else
339 timeBox->setValue(100);
340 timeBox_valueChanged(100);
341 timeBox->setEnabled(false);
342 timeSlider->setEnabled(false);
344 if (m_voiceList[index].pitchAdjustable)
346 frequencyBox->setEnabled(true);
347 frequencySlider->setEnabled(true);
349 else
351 frequencyBox->setValue(100);
352 frequencyBox_valueChanged(100);
353 frequencyBox->setEnabled(false);
354 frequencySlider->setEnabled(false);
356 if ((int)index != currentVoiceIndex) configChanged();
362 * Given an XML node and child element name, returns the string value from the child element.
363 * If no such child element, returns def.
365 QString FestivalIntConf::readXmlString(QDomNode &node, const QString &elementName, const QString &def)
367 QDomNode childNode = node.namedItem(elementName);
368 if (!childNode.isNull())
369 return childNode.toElement().text();
370 else
371 return def;
375 * Given an XML node and child element name, returns the boolean value from the child element.
376 * If no such child element, returns def.
378 bool FestivalIntConf::readXmlBool(QDomNode &node, const QString &elementName, bool def)
380 QDomNode childNode = node.namedItem(elementName);
381 if (!childNode.isNull())
382 return (childNode.toElement().text() == "true");
383 else
384 return def;
387 void FestivalIntConf::scanVoices()
389 // kDebug() << "FestivalIntConf::scanVoices: Running";
390 // Get existing voice code (if any).
391 QString currentVoiceCode;
392 int index = selectVoiceCombo->currentIndex();
393 if (index >= 0 && index < m_voiceList.count()) currentVoiceCode = m_voiceList[index].code;
395 m_voiceList.clear();
396 selectVoiceCombo->clear();
397 selectVoiceCombo->addItem(i18n("Scanning... Please wait."));
399 // Save current state of selectVoiceCombo box and disable.
400 bool selectVoiceComboEnabled = selectVoiceCombo->isEnabled();
401 selectVoiceCombo->setEnabled(false);
403 // Clear existing list of supported voice codes.
404 // m_supportedVoiceCodes.clear();
405 selectVoiceCombo->clear();
407 QString exePath = realFilePath(festivalPath->url().path());
408 if (!getLocation(exePath).isEmpty())
410 // Set up a progress dialog.
411 m_progressDlg = new KProgressDialog(this,
412 i18n("Query Voices"),
413 i18n("Querying Festival for available voices. This could take up to 15 seconds."));
414 m_progressDlg->setModal(true);
415 m_progressDlg->progressBar()->hide();
416 m_progressDlg->setAllowCancel(true);
417 // TODO: This is a bug workaround. Remove when no longer needed.
418 m_progressDlg->setDefaultButton(KDialog::Cancel);
420 // Create Festival process and request a list of voice codes.
421 if (m_festProc)
422 m_festProc->stopText();
423 else
425 m_festProc = new FestivalIntProc();
426 connect (m_festProc, SIGNAL(stopped()), this, SLOT(slotSynthStopped()));
428 connect (m_festProc, SIGNAL(queryVoicesFinished(const QStringList&)),
429 this, SLOT(slotQueryVoicesFinished(const QStringList&)));
430 m_festProc->queryVoices(exePath);
432 // Display progress dialog modally.
433 m_progressDlg->exec();
434 // kDebug() << "FestivalIntConf::scanVoices: back from progressDlg->exec()";
436 // Processing continues until either user clicks Cancel button, or until
437 // Festival responds with the list. When Festival responds with list,
438 // the progress dialog is closed.
440 disconnect (m_festProc, SIGNAL(queryVoicesFinished(const QStringList&)),
441 this, SLOT(slotQueryVoicesFinished(const QStringList&)));
442 if (!m_progressDlg->wasCancelled()) m_festProc->stopText();
443 delete m_progressDlg;
444 m_progressDlg = 0;
445 m_supportsSSML = m_festProc->supportsSSML();
448 if (!m_supportedVoiceCodes.isEmpty())
450 // User's desktop language setting.
451 QString desktopLanguageCode = KGlobal::locale()->language();
452 QString langAlpha;
453 QString countryCode;
454 QString modifier;
455 QString charSet;
456 KGlobal::locale()->splitLocale(desktopLanguageCode, langAlpha, countryCode, modifier, charSet);
457 desktopLanguageCode = langAlpha.toLower();
459 // Festival known voices list.
460 QString voicesFilename = KGlobal::dirs()->resourceDirs("data").last() + "/kttsd/festivalint/voices";
461 QDomDocument voicesDoc("Festival Voices");
462 QFile voicesFile(voicesFilename);
463 if (voicesFile.open(QIODevice::ReadOnly)) voicesDoc.setContent(&voicesFile);
464 voicesFile.close();
465 QDomNodeList voices = voicesDoc.elementsByTagName("voice");
466 uint voicesCount = voices.count();
467 if (voicesCount == 0)
468 kDebug() << "FestivalIntConf::scanVoices: Unable to open " << voicesFilename << ". Is KDEDIR defined?";
470 // Iterate thru list of voice codes returned by Festival,
471 // find matching entry in voices.xml file, and add to list of supported voices.
472 KIcon maleIcon("male");
473 KIcon femaleIcon("female");
474 QStringList::ConstIterator itEnd = m_supportedVoiceCodes.constEnd();
475 for(QStringList::ConstIterator it = m_supportedVoiceCodes.constBegin(); it != itEnd; ++it )
477 QString code = *it;
478 bool found = false;
479 for (uint index=0; index < voicesCount; ++index)
481 QDomNode voiceNode = voices.item(index);
482 QString voiceCode = readXmlString(voiceNode, "code", QString());
483 // kDebug() << "FestivalIntConf::scanVoices: Comparing code " << code << " to " << voiceCode;
484 if (voiceCode == code)
486 found = true;
487 voiceStruct voiceTemp;
488 voiceTemp.code = code;
489 voiceTemp.name = i18nc("FestivalVoiceName",
490 readXmlString(voiceNode, "name", "Unknown").toUtf8());
491 voiceTemp.languageCode = readXmlString(voiceNode, "language", m_languageCode);
492 voiceTemp.codecName = readXmlString(voiceNode, "codec", "ISO 8859-1");
493 voiceTemp.gender = readXmlString(voiceNode, "gender", "neutral");
494 voiceTemp.preload = readXmlBool(voiceNode, "preload", false);
495 voiceTemp.volumeAdjustable = readXmlBool(voiceNode, "volume-adjustable", true);
496 voiceTemp.rateAdjustable = readXmlBool(voiceNode, "rate-adjustable", true);
497 voiceTemp.pitchAdjustable = readXmlBool(voiceNode, "pitch-adjustable", true);
498 m_voiceList.append(voiceTemp);
499 QString voiceDisplayName = voiceTemp.name + " (" + voiceTemp.code + ')';
500 if (voiceTemp.gender == "male")
501 selectVoiceCombo->addItem(maleIcon, voiceDisplayName);
502 else if (voiceTemp.gender == "female")
503 selectVoiceCombo->addItem(femaleIcon, voiceDisplayName);
504 else
505 selectVoiceCombo->addItem(voiceDisplayName);
506 break;
509 if (!found)
511 voiceStruct voiceTemp;
512 voiceTemp.code = code;
513 voiceTemp.name = i18n("Unknown");
514 voiceTemp.languageCode = m_languageCode;
515 voiceTemp.codecName = "ISO 8858-1";
516 voiceTemp.gender = "neutral";
517 voiceTemp.preload = false;
518 voiceTemp.volumeAdjustable = true;
519 voiceTemp.rateAdjustable = true;
520 voiceTemp.pitchAdjustable = true;
521 m_voiceList.append(voiceTemp);
522 selectVoiceCombo->addItem(voiceTemp.name + " (" + voiceTemp.code + ')');
525 selectVoiceCombo->setEnabled(true);
526 } else kDebug() << "FestivalIntConf::scanVoices: No voices found";
527 setDefaultVoice(voiceCodeToListIndex(currentVoiceCode));
528 // Emit configChanged if the enabled state of the selectVoiceCombo has changed.
529 // This occurs when user changes Festival EXE path, then clicks Rescan.
530 if (selectVoiceComboEnabled != selectVoiceCombo->isEnabled()) configChanged();
533 void FestivalIntConf::slotQueryVoicesFinished(const QStringList &voiceCodes)
535 // kDebug() << "FestivalIntConf::slotQueryVoicesFinished: voiceCodes.count() = " << voiceCodes.count();
536 m_supportedVoiceCodes = voiceCodes;
537 if (m_progressDlg) m_progressDlg->close();
540 void FestivalIntConf::slotTest_clicked()
542 // kDebug() << "FestivalIntConf::slotTest_clicked: Running ";
543 // If currently synthesizing, stop it.
544 if (m_festProc)
545 m_festProc->stopText();
546 else
548 m_festProc = new FestivalIntProc();
549 connect (m_festProc, SIGNAL(stopped()), this, SLOT(slotSynthStopped()));
551 // Create a temp file name for the wave file.
552 KTemporaryFile *tempFile = new KTemporaryFile();
553 tempFile->setPrefix("festivalintplugin-");
554 tempFile->setSuffix(".wav");
555 tempFile->open();
556 QString tmpWaveFile = tempFile->fileName();
557 delete tempFile;
559 kDebug() << "FestivalIntConf::slotTest_clicked: tmpWaveFile = " << tmpWaveFile;
561 // Get the code for the selected voice.
562 QString voiceCode = m_voiceList[selectVoiceCombo->currentIndex()].code;
564 // Get language code for the selected voice.
565 QString languageCode = m_voiceList[selectVoiceCombo->currentIndex()].languageCode;
567 // Get test message in the language of the voice.
568 QString testMsg = testMessage(languageCode);
570 // Get codec.
571 QTextCodec* codec = PlugInProc::codecIndexToCodec(
572 characterCodingBox->currentIndex(), m_codecList);
574 // Tell user to wait.
575 m_progressDlg = new KProgressDialog(this,
576 i18n("Testing"),
577 i18n("Testing. MultiSyn voices require several seconds to load. Please be patient."));
578 m_progressDlg->setModal(true);
579 m_progressDlg->progressBar()->hide();
580 m_progressDlg->setAllowCancel(true);
582 // kDebug() << "FestivalIntConf::slotTest_clicked: calling synth with voiceCode: " << voiceCode << " time percent: " << timeBox->value();
583 connect (m_festProc, SIGNAL(synthFinished()), this, SLOT(slotSynthFinished()));
584 m_festProc->synth(
585 realFilePath(festivalPath->url().path()),
586 testMsg,
587 tmpWaveFile,
588 voiceCode,
589 timeBox->value(),
590 frequencyBox->value(),
591 volumeBox->value(),
592 languageCode,
593 codec);
595 // Display progress dialog modally. Processing continues when plugin signals synthFinished,
596 // or if user clicks Cancel button.
597 m_progressDlg->exec();
598 disconnect (m_festProc, SIGNAL(synthFinished()), this, SLOT(slotSynthFinished()));
599 if (m_progressDlg->wasCancelled()) m_festProc->stopText();
600 delete m_progressDlg;
601 m_progressDlg = 0;
604 void FestivalIntConf::slotSynthFinished()
606 // kDebug() << "FestivalIntConf::slotSynthFinished: Running";
607 // If user canceled, progress dialog is gone, so exit.
608 if (!m_progressDlg)
610 m_festProc->ackFinished();
611 return;
613 // Hide the Cancel button so user can't cancel in the middle of playback.
614 m_progressDlg->showCancelButton(false);
615 // Get new wavefile name.
616 m_waveFile = m_festProc->getFilename();
617 // Tell synth we're done.
618 m_festProc->ackFinished();
619 // Play the wave file (possibly adjusting its Speed).
620 // Player object deletes the wave file when done.
621 if (m_player) m_player->play(m_waveFile);
622 QFile::remove(m_waveFile);
623 m_waveFile.clear();
624 if (m_progressDlg) m_progressDlg->close();
627 void FestivalIntConf::slotSynthStopped()
629 // Clean up after canceling test.
630 QString filename = m_festProc->getFilename();
631 // kDebug() << "FestivalIntConf::slotSynthStopped: filename = " << filename;
632 if (!filename.isNull()) QFile::remove(filename);
635 void FestivalIntConf::slotFestivalPath_textChanged()
637 QString exePath = realFilePath(festivalPath->url().path());
638 selectVoiceCombo->setEnabled(false);
639 if (!exePath.isEmpty() && !getLocation(exePath).isEmpty())
641 rescan->setEnabled(true);
642 } else rescan->setEnabled(false);
645 void FestivalIntConf::slotSelectVoiceCombo_activated()
647 int index = selectVoiceCombo->currentIndex();
648 QString codecName = m_voiceList[index].codecName;
649 int codecNdx = PlugInProc::codecNameToListIndex(codecName, m_codecList);
650 characterCodingBox->setCurrentIndex(codecNdx);
651 preloadCheckBox->setChecked(
652 m_voiceList[index].preload);
653 if (m_voiceList[index].volumeAdjustable)
655 volumeBox->setEnabled(true);
656 volumeSlider->setEnabled(true);
658 else
660 volumeBox->setValue(100);
661 volumeBox_valueChanged(100);
662 volumeBox->setEnabled(false);
663 volumeSlider->setEnabled(false);
665 if (m_voiceList[index].rateAdjustable)
667 timeBox->setEnabled(true);
668 timeSlider->setEnabled(true);
670 else
672 timeBox->setValue(100);
673 timeBox_valueChanged(100);
674 timeBox->setEnabled(false);
675 timeSlider->setEnabled(false);
677 if (m_voiceList[index].pitchAdjustable)
679 frequencyBox->setEnabled(true);
680 frequencySlider->setEnabled(true);
682 else
684 frequencyBox->setValue(100);
685 frequencyBox_valueChanged(100);
686 frequencyBox->setEnabled(false);
687 frequencySlider->setEnabled(false);
691 // Basically the slider values are logarithmic (0,...,1000) whereas percent
692 // values are linear (50%,...,200%).
694 // slider = alpha * (log(percent)-log(50))
695 // with alpha = 1000/(log(200)-log(50))
697 int FestivalIntConf::percentToSlider(int percentValue) {
698 double alpha = 1000 / (log(200.0) - log(50.0));
699 return (int)floor (0.5 + alpha * (log((float)percentValue)-log(50.0)));
702 int FestivalIntConf::sliderToPercent(int sliderValue) {
703 double alpha = 1000 / (log(200.0) - log(50.0));
704 return (int)floor(0.5 + exp (sliderValue/alpha + log(50.0)));
707 void FestivalIntConf::volumeBox_valueChanged(int percentValue) {
708 volumeSlider->setValue(percentToSlider(percentValue));
711 void FestivalIntConf::timeBox_valueChanged(int percentValue) {
712 timeSlider->setValue (percentToSlider (percentValue));
715 void FestivalIntConf::frequencyBox_valueChanged(int percentValue) {
716 frequencySlider->setValue(percentToSlider(percentValue));
719 void FestivalIntConf::volumeSlider_valueChanged(int sliderValue) {
720 volumeBox->setValue(sliderToPercent(sliderValue));
723 void FestivalIntConf::timeSlider_valueChanged(int sliderValue) {
724 timeBox->setValue (sliderToPercent (sliderValue));
727 void FestivalIntConf::frequencySlider_valueChanged(int sliderValue) {
728 frequencyBox->setValue(sliderToPercent(sliderValue));