Fix include
[kdeaccessibility.git] / kttsd / plugins / festivalint / festivalintconf.cpp
blob04f0e9acf620dd167a6df1d2a9d6b7f7d6c30f16
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 // C++ includes.
25 #include <math.h>
27 // Qt includes.
28 #include <QLabel>
30 #include <QCheckBox>
31 #include <QDir>
32 #include <QSlider>
33 #include <qdom.h>
34 #include <QTextCodec>
35 #include <QPixmap>
37 // KDE includes.
38 #include <kdebug.h>
39 #include <klocale.h>
40 #include <kcombobox.h>
41 #include <kglobal.h>
42 #include <ktemporaryfile.h>
43 #include <kstandarddirs.h>
44 #include <knuminput.h>
45 #include <k3procio.h>
46 #include <kprogressdialog.h>
47 #include <kicon.h>
49 // KTTS includes.
50 #include "testplayer.h"
52 // FestivalInt includes.
53 #include "festivalintproc.h"
54 #include "festivalintconf.h"
55 #include "festivalintconf.moc"
57 /** Constructor */
58 FestivalIntConf::FestivalIntConf( QWidget* parent, const QStringList& /*args*/) :
59 PlugInConf(parent, "festivalintconf")
61 // kDebug() << "FestivalIntConf::FestivalIntConf: Running" << endl;
62 m_festProc = 0;
63 m_progressDlg = 0;
64 m_supportsSSML = FestivalIntProc::ssUnknown;
66 setupUi(this);
68 festivalPath->setMode(KFile::File | KFile::ExistingOnly);
69 festivalPath->setFilter("*");
71 // Build codec list and fill combobox.
72 m_codecList = PlugInProc::buildCodecList();
73 characterCodingBox->clear();
74 characterCodingBox->addItems(m_codecList);
76 // defaults();
78 connect(festivalPath, SIGNAL(textChanged(const QString&)),
79 this, SLOT(slotFestivalPath_textChanged()));
80 connect(selectVoiceCombo, SIGNAL(activated(const QString&)),
81 this, SLOT(slotSelectVoiceCombo_activated()));
82 connect(selectVoiceCombo, SIGNAL(activated(const QString&)),
83 this, SLOT(configChanged()));
84 connect(testButton, SIGNAL(clicked()), this, SLOT(slotTest_clicked()));
85 connect(rescan, SIGNAL(clicked()), this, SLOT(scanVoices()));
86 connect(volumeBox, SIGNAL(valueChanged(int)),
87 this, SLOT(volumeBox_valueChanged(int)));
88 connect(timeBox, SIGNAL(valueChanged(int)),
89 this, SLOT(timeBox_valueChanged(int)));
90 connect(frequencyBox, SIGNAL(valueChanged(int)),
91 this, SLOT(frequencyBox_valueChanged(int)));
92 connect(volumeSlider, SIGNAL(valueChanged(int)),
93 this, SLOT(volumeSlider_valueChanged(int)));
94 connect(timeSlider, SIGNAL(valueChanged(int)),
95 this, SLOT(timeSlider_valueChanged(int)));
96 connect(frequencySlider, SIGNAL(valueChanged(int)),
97 this, SLOT(frequencySlider_valueChanged(int)));
98 connect(volumeBox, SIGNAL(valueChanged(int)), this, SLOT(configChanged()));
99 connect(volumeSlider, SIGNAL(valueChanged(int)), this, SLOT(configChanged()));
100 connect(timeBox, SIGNAL(valueChanged(int)), this, SLOT(configChanged()));
101 connect(timeSlider, SIGNAL(valueChanged(int)), this, SLOT(configChanged()));
102 connect(frequencyBox, SIGNAL(valueChanged(int)), this, SLOT(configChanged()));
103 connect(frequencySlider, SIGNAL(valueChanged(int)), this, SLOT(configChanged()));
104 connect(preloadCheckBox, SIGNAL(clicked()), this, SLOT(configChanged()));
105 connect(characterCodingBox, SIGNAL(textChanged(const QString&)),
106 this, SLOT(configChanged()));
107 connect(characterCodingBox, SIGNAL(activated(const QString&)),
108 this, SLOT(configChanged()));
111 /** Destructor */
112 FestivalIntConf::~FestivalIntConf(){
113 // kDebug() << "FestivalIntConf::~FestivalIntConf: Running" << endl;
114 if (!m_waveFile.isNull()) QFile::remove(m_waveFile);
115 delete m_festProc;
116 delete m_progressDlg;
120 * Given a voice code, returns index into m_voiceList array (and voiceCombo box).
121 * -1 if not found.
123 int FestivalIntConf::voiceCodeToListIndex(const QString& voiceCode) const
125 for (int i = 0; i < m_voiceList.size(); ++i)
126 if (m_voiceList[i].code == voiceCode) return i;
127 return -1;
130 void FestivalIntConf::load(KConfig *c, const QString &configGroup){
131 //kDebug() << "FestivalIntConf::load: Running" << endl;
132 KConfigGroup festivalConfig(c, "FestivalInt");
133 QString exePath = festivalConfig.readEntry("FestivalExecutablePath", "festival");
134 QString exeLocation = getLocation(exePath);
135 if (!exeLocation.isEmpty()) exePath = exeLocation;
136 exePath = realFilePath(exePath);
138 KConfigGroup config(c, configGroup);
139 festivalPath->setUrl(KUrl::fromPath(config.readEntry("FestivalExecutablePath", exePath)));
140 preloadCheckBox->setChecked(false);
141 scanVoices();
142 QString voiceSelected(config.readEntry("Voice"));
143 int index = voiceCodeToListIndex(voiceSelected);
144 if (index >= 0)
146 selectVoiceCombo->setCurrentIndex(index);
147 preloadCheckBox->setChecked(m_voiceList[index].preload);
149 volumeBox->setValue(config.readEntry("volume", 100));
150 timeBox->setValue(config.readEntry("time", 100));
151 frequencyBox->setValue(config.readEntry("pitch", 100));
152 preloadCheckBox->setChecked(config.readEntry(
153 "Preload", preloadCheckBox->isChecked()));
154 m_languageCode = config.readEntry("LanguageCode", m_languageCode);
155 m_supportsSSML = static_cast<FestivalIntProc::SupportsSSML>(
156 config.readEntry("SupportsSSML", int(FestivalIntProc::ssUnknown)));
157 QString codecName = PlugInProc::codecIndexToCodecName(
158 characterCodingBox->currentIndex(), m_codecList);
159 codecName = config.readEntry("Codec", codecName);
160 int codecNdx = PlugInProc::codecNameToListIndex(codecName, m_codecList);
161 characterCodingBox->setCurrentIndex(codecNdx);
164 void FestivalIntConf::save(KConfig *c, const QString &configGroup){
165 // kDebug() << "FestivalIntConf::save: Running" << endl;
166 KConfigGroup festivalConfig(c, "FestivalInt");
167 festivalConfig.writeEntry("FestivalExecutablePath", realFilePath(festivalPath->url().path()));
169 KConfigGroup config(c, configGroup);
170 config.writeEntry("FestivalExecutablePath", realFilePath(festivalPath->url().path()));
171 config.writeEntry("Voice", m_voiceList[selectVoiceCombo->currentIndex()].code);
172 config.writeEntry("volume", volumeBox->value());
173 config.writeEntry("time", timeBox->value());
174 config.writeEntry("pitch", frequencyBox->value());
175 config.writeEntry("Preload", preloadCheckBox->isChecked());
176 config.writeEntry("LanguageCode", m_voiceList[selectVoiceCombo->currentIndex()].languageCode);
177 config.writeEntry("SupportsSSML", int(m_supportsSSML));
178 int codec = characterCodingBox->currentIndex();
179 config.writeEntry("Codec", PlugInProc::codecIndexToCodecName(codec, m_codecList));
182 void FestivalIntConf::defaults(){
183 // kDebug() << "FestivalIntConf::defaults: Running" << endl;
184 festivalPath->setUrl(KUrl("festival"));
185 timeBox->setValue(100);
186 timeBox_valueChanged(100);
187 volumeBox->setValue(100);
188 volumeBox_valueChanged(100);
189 frequencyBox->setValue(100);
190 frequencyBox_valueChanged(100);
191 preloadCheckBox->setChecked(false);
192 characterCodingBox->setCurrentIndex(
193 PlugInProc::codecNameToListIndex("ISO 8859-1", m_codecList));
194 scanVoices();
197 void FestivalIntConf::setDesiredLanguage(const QString &lang)
199 // kDebug() << "FestivalIntConf::setDesiredLanguage: Running" << endl;
200 m_languageCode = splitLanguageCode(lang, m_countryCode);
203 QString FestivalIntConf::getTalkerCode()
205 if (!selectVoiceCombo->isEnabled()) return QString();
206 QString exePath = realFilePath(festivalPath->url().path());
207 if (exePath.isEmpty()) return QString();
208 if (getLocation(exePath).isEmpty()) return QString();
209 if (m_voiceList.count() == 0) return QString();
210 QString normalTalkerCode;
211 voiceStruct voiceTemp = m_voiceList[selectVoiceCombo->currentIndex()];
212 // Determine volume attribute. soft < 75% <= medium <= 125% < loud.
213 QString volume = "medium";
214 if (volumeBox->value() < 75) volume = "soft";
215 if (volumeBox->value() > 125) volume = "loud";
216 // Determine rate attribute. slow < 75% <= medium <= 125% < fast.
217 QString rate = "medium";
218 if (timeBox->value() < 75) rate = "slow";
219 if (timeBox->value() > 125) rate = "fast";
220 normalTalkerCode = QString(
221 "<voice lang=\"%1\" name=\"%2\" gender=\"%3\" />"
222 "<prosody volume=\"%4\" rate=\"%5\" />"
223 "<kttsd synthesizer=\"%6\" />")
224 .arg(voiceTemp.languageCode)
225 .arg(voiceTemp.code)
226 .arg(voiceTemp.gender)
227 .arg(volume)
228 .arg(rate)
229 .arg("Festival Interactive");
230 return normalTalkerCode;
234 * Chooses a default voice given scanned list of voices in m_voiceList and current
235 * language and country code, and updates controls.
236 * @param currentVoiceIndex This voice is preferred if it matches.
238 void FestivalIntConf::setDefaultVoice(int currentVoiceIndex)
240 // kDebug() << "FestivalIntCont::setDefaultVoice: Running" << endl;
241 // If language code is known, auto pick first voice that matches the language code.
242 if (!m_languageCode.isEmpty())
244 bool found = false;
245 // First search for a match on both language code and country code.
246 QString languageCode = m_languageCode;
247 if (!m_countryCode.isNull()) languageCode += '_' + m_countryCode;
248 // kDebug() << "FestivalIntConf::setDefaultVoice:: looking for default voice to match language code " << languageCode << endl;
249 int index = 0;
250 // Prefer existing voice if it matches.
251 if (currentVoiceIndex >= 0)
253 QString vlCode = m_voiceList[currentVoiceIndex].languageCode.left(languageCode.length());
254 if (languageCode == vlCode)
256 found = true;
257 index = currentVoiceIndex;
260 if (!found)
262 for(index = 0 ; index < m_voiceList.count(); ++index)
264 QString vlCode = m_voiceList[index].languageCode.left(languageCode.length());
265 // kDebug() << "FestivalIntConf::setDefaultVoice: testing " << vlCode << endl;
266 if(languageCode == vlCode)
268 found = true;
269 break;
273 // If not found, search for a match on just the language code.
274 if (!found)
276 languageCode = m_languageCode;
277 // Prefer existing voice if it matches.
278 if (currentVoiceIndex >= 0)
280 QString vlCode = m_voiceList[currentVoiceIndex].languageCode.left(languageCode.length());
281 if (languageCode == vlCode)
283 found = true;
284 index = currentVoiceIndex;
287 if (!found)
289 for(index = 0 ; index < m_voiceList.count(); ++index)
291 QString vlCode = m_voiceList[index].languageCode.left(languageCode.length());
292 // kDebug() << "FestivalIntConf::setDefaultVoice: testing " << vlCode << endl;
293 if(languageCode == vlCode)
295 found = true;
296 break;
301 // If not found, pick first voice that is not "Unknown".
302 if (!found)
304 for(index = 0 ; index < m_voiceList.count(); ++index)
306 if (m_voiceList[index].name != i18n("Unknown"))
308 found = true;
309 break;
313 if (found)
315 // kDebug() << "FestivalIntConf::setDefaultVoice: auto picking voice code " << m_voiceList[index].code << endl;
316 selectVoiceCombo->setCurrentIndex(index);
317 preloadCheckBox->setChecked(m_voiceList[index].preload);
318 QString codecName = m_voiceList[index].codecName;
319 int codecNdx = PlugInProc::codecNameToListIndex(codecName, m_codecList);
320 characterCodingBox->setCurrentIndex(codecNdx);
321 if (m_voiceList[index].volumeAdjustable)
323 volumeBox->setEnabled(true);
324 volumeSlider->setEnabled(true);
326 else
328 volumeBox->setValue(100);
329 volumeBox_valueChanged(100);
330 volumeBox->setEnabled(false);
331 volumeSlider->setEnabled(false);
333 if (m_voiceList[index].rateAdjustable)
335 timeBox->setEnabled(true);
336 timeSlider->setEnabled(true);
338 else
340 timeBox->setValue(100);
341 timeBox_valueChanged(100);
342 timeBox->setEnabled(false);
343 timeSlider->setEnabled(false);
345 if (m_voiceList[index].pitchAdjustable)
347 frequencyBox->setEnabled(true);
348 frequencySlider->setEnabled(true);
350 else
352 frequencyBox->setValue(100);
353 frequencyBox_valueChanged(100);
354 frequencyBox->setEnabled(false);
355 frequencySlider->setEnabled(false);
357 if ((int)index != currentVoiceIndex) configChanged();
363 * Given an XML node and child element name, returns the string value from the child element.
364 * If no such child element, returns def.
366 QString FestivalIntConf::readXmlString(QDomNode &node, const QString &elementName, const QString &def)
368 QDomNode childNode = node.namedItem(elementName);
369 if (!childNode.isNull())
370 return childNode.toElement().text();
371 else
372 return def;
376 * Given an XML node and child element name, returns the boolean value from the child element.
377 * If no such child element, returns def.
379 bool FestivalIntConf::readXmlBool(QDomNode &node, const QString &elementName, bool def)
381 QDomNode childNode = node.namedItem(elementName);
382 if (!childNode.isNull())
383 return (childNode.toElement().text() == "true");
384 else
385 return def;
388 void FestivalIntConf::scanVoices()
390 // kDebug() << "FestivalIntConf::scanVoices: Running" << endl;
391 // Get existing voice code (if any).
392 QString currentVoiceCode;
393 int index = selectVoiceCombo->currentIndex();
394 if (index >= 0 && index < m_voiceList.count()) currentVoiceCode = m_voiceList[index].code;
396 m_voiceList.clear();
397 selectVoiceCombo->clear();
398 selectVoiceCombo->addItem(i18n("Scanning... Please wait."));
400 // Save current state of selectVoiceCombo box and disable.
401 bool selectVoiceComboEnabled = selectVoiceCombo->isEnabled();
402 selectVoiceCombo->setEnabled(false);
404 // Clear existing list of supported voice codes.
405 // m_supportedVoiceCodes.clear();
406 selectVoiceCombo->clear();
408 QString exePath = realFilePath(festivalPath->url().path());
409 if (!getLocation(exePath).isEmpty())
411 // Set up a progress dialog.
412 m_progressDlg = new KProgressDialog(this,
413 i18n("Query Voices"),
414 i18n("Querying Festival for available voices. This could take up to 15 seconds."));
415 m_progressDlg->setModal(true);
416 m_progressDlg->progressBar()->hide();
417 m_progressDlg->setAllowCancel(true);
418 // TODO: This is a bug workaround. Remove when no longer needed.
419 m_progressDlg->setDefaultButton(KDialog::Cancel);
421 // Create Festival process and request a list of voice codes.
422 if (m_festProc)
423 m_festProc->stopText();
424 else
426 m_festProc = new FestivalIntProc();
427 connect (m_festProc, SIGNAL(stopped()), this, SLOT(slotSynthStopped()));
429 connect (m_festProc, SIGNAL(queryVoicesFinished(const QStringList&)),
430 this, SLOT(slotQueryVoicesFinished(const QStringList&)));
431 m_festProc->queryVoices(exePath);
433 // Display progress dialog modally.
434 m_progressDlg->exec();
435 // kDebug() << "FestivalIntConf::scanVoices: back from progressDlg->exec()" << endl;
437 // Processing continues until either user clicks Cancel button, or until
438 // Festival responds with the list. When Festival responds with list,
439 // the progress dialog is closed.
441 disconnect (m_festProc, SIGNAL(queryVoicesFinished(const QStringList&)),
442 this, SLOT(slotQueryVoicesFinished(const QStringList&)));
443 if (!m_progressDlg->wasCancelled()) m_festProc->stopText();
444 delete m_progressDlg;
445 m_progressDlg = 0;
446 m_supportsSSML = m_festProc->supportsSSML();
449 if (!m_supportedVoiceCodes.isEmpty())
451 // User's desktop language setting.
452 QString desktopLanguageCode = KGlobal::locale()->language();
453 QString langAlpha;
454 QString countryCode;
455 QString modifier;
456 QString charSet;
457 KGlobal::locale()->splitLocale(desktopLanguageCode, langAlpha, countryCode, modifier, charSet);
458 desktopLanguageCode = langAlpha.toLower();
460 // Festival known voices list.
461 QString voicesFilename = KGlobal::dirs()->resourceDirs("data").last() + "/kttsd/festivalint/voices";
462 QDomDocument voicesDoc("Festival Voices");
463 QFile voicesFile(voicesFilename);
464 if (voicesFile.open(QIODevice::ReadOnly)) voicesDoc.setContent(&voicesFile);
465 voicesFile.close();
466 QDomNodeList voices = voicesDoc.elementsByTagName("voice");
467 uint voicesCount = voices.count();
468 if (voicesCount == 0)
469 kDebug() << "FestivalIntConf::scanVoices: Unable to open " << voicesFilename << ". Is KDEDIR defined?" << endl;
471 // Iterate thru list of voice codes returned by Festival,
472 // find matching entry in voices.xml file, and add to list of supported voices.
473 KIcon maleIcon("male");
474 KIcon femaleIcon("female");
475 QStringList::ConstIterator itEnd = m_supportedVoiceCodes.constEnd();
476 for(QStringList::ConstIterator it = m_supportedVoiceCodes.begin(); it != itEnd; ++it )
478 QString code = *it;
479 bool found = false;
480 for (uint index=0; index < voicesCount; ++index)
482 QDomNode voiceNode = voices.item(index);
483 QString voiceCode = readXmlString(voiceNode, "code", QString());
484 // kDebug() << "FestivalIntConf::scanVoices: Comparing code " << code << " to " << voiceCode << endl;
485 if (voiceCode == code)
487 found = true;
488 voiceStruct voiceTemp;
489 voiceTemp.code = code;
490 voiceTemp.name = i18nc("FestivalVoiceName",
491 readXmlString(voiceNode, "name", "Unknown").toUtf8());
492 voiceTemp.languageCode = readXmlString(voiceNode, "language", m_languageCode);
493 voiceTemp.codecName = readXmlString(voiceNode, "codec", "ISO 8859-1");
494 voiceTemp.gender = readXmlString(voiceNode, "gender", "neutral");
495 voiceTemp.preload = readXmlBool(voiceNode, "preload", false);
496 voiceTemp.volumeAdjustable = readXmlBool(voiceNode, "volume-adjustable", true);
497 voiceTemp.rateAdjustable = readXmlBool(voiceNode, "rate-adjustable", true);
498 voiceTemp.pitchAdjustable = readXmlBool(voiceNode, "pitch-adjustable", true);
499 m_voiceList.append(voiceTemp);
500 QString voiceDisplayName = voiceTemp.name + " (" + voiceTemp.code + ')';
501 if (voiceTemp.gender == "male")
502 selectVoiceCombo->addItem(maleIcon, voiceDisplayName);
503 else if (voiceTemp.gender == "female")
504 selectVoiceCombo->addItem(femaleIcon, voiceDisplayName);
505 else
506 selectVoiceCombo->addItem(voiceDisplayName);
507 break;
510 if (!found)
512 voiceStruct voiceTemp;
513 voiceTemp.code = code;
514 voiceTemp.name = i18n("Unknown");
515 voiceTemp.languageCode = m_languageCode;
516 voiceTemp.codecName = "ISO 8858-1";
517 voiceTemp.gender = "neutral";
518 voiceTemp.preload = false;
519 voiceTemp.volumeAdjustable = true;
520 voiceTemp.rateAdjustable = true;
521 voiceTemp.pitchAdjustable = true;
522 m_voiceList.append(voiceTemp);
523 selectVoiceCombo->addItem(voiceTemp.name + " (" + voiceTemp.code + ')');
526 selectVoiceCombo->setEnabled(true);
527 } else kDebug() << "FestivalIntConf::scanVoices: No voices found" << endl;
528 setDefaultVoice(voiceCodeToListIndex(currentVoiceCode));
529 // Emit configChanged if the enabled state of the selectVoiceCombo has changed.
530 // This occurs when user changes Festival EXE path, then clicks Rescan.
531 if (selectVoiceComboEnabled != selectVoiceCombo->isEnabled()) configChanged();
534 void FestivalIntConf::slotQueryVoicesFinished(const QStringList &voiceCodes)
536 // kDebug() << "FestivalIntConf::slotQueryVoicesFinished: voiceCodes.count() = " << voiceCodes.count() << endl;
537 m_supportedVoiceCodes = voiceCodes;
538 if (m_progressDlg) m_progressDlg->close();
541 void FestivalIntConf::slotTest_clicked()
543 // kDebug() << "FestivalIntConf::slotTest_clicked: Running " << endl;
544 // If currently synthesizing, stop it.
545 if (m_festProc)
546 m_festProc->stopText();
547 else
549 m_festProc = new FestivalIntProc();
550 connect (m_festProc, SIGNAL(stopped()), this, SLOT(slotSynthStopped()));
552 // Create a temp file name for the wave file.
553 KTemporaryFile *tempFile = new KTemporaryFile();
554 tempFile->setPrefix("festivalintplugin-");
555 tempFile->setSuffix(".wav");
556 tempFile->open();
557 QString tmpWaveFile = tempFile->fileName();
558 delete tempFile;
560 kDebug() << "FestivalIntConf::slotTest_clicked: tmpWaveFile = " << tmpWaveFile << endl;
562 // Get the code for the selected voice.
563 QString voiceCode = m_voiceList[selectVoiceCombo->currentIndex()].code;
565 // Get language code for the selected voice.
566 QString languageCode = m_voiceList[selectVoiceCombo->currentIndex()].languageCode;
568 // Get test message in the language of the voice.
569 QString testMsg = testMessage(languageCode);
571 // Get codec.
572 QTextCodec* codec = PlugInProc::codecIndexToCodec(
573 characterCodingBox->currentIndex(), m_codecList);
575 // Tell user to wait.
576 m_progressDlg = new KProgressDialog(this,
577 i18n("Testing"),
578 i18n("Testing. MultiSyn voices require several seconds to load. Please be patient."));
579 m_progressDlg->setModal(true);
580 m_progressDlg->progressBar()->hide();
581 m_progressDlg->setAllowCancel(true);
583 // kDebug() << "FestivalIntConf::slotTest_clicked: calling synth with voiceCode: " << voiceCode << " time percent: " << timeBox->value() << endl;
584 connect (m_festProc, SIGNAL(synthFinished()), this, SLOT(slotSynthFinished()));
585 m_festProc->synth(
586 realFilePath(festivalPath->url().path()),
587 testMsg,
588 tmpWaveFile,
589 voiceCode,
590 timeBox->value(),
591 frequencyBox->value(),
592 volumeBox->value(),
593 languageCode,
594 codec);
596 // Display progress dialog modally. Processing continues when plugin signals synthFinished,
597 // or if user clicks Cancel button.
598 m_progressDlg->exec();
599 disconnect (m_festProc, SIGNAL(synthFinished()), this, SLOT(slotSynthFinished()));
600 if (m_progressDlg->wasCancelled()) m_festProc->stopText();
601 delete m_progressDlg;
602 m_progressDlg = 0;
605 void FestivalIntConf::slotSynthFinished()
607 // kDebug() << "FestivalIntConf::slotSynthFinished: Running" << endl;
608 // If user canceled, progress dialog is gone, so exit.
609 if (!m_progressDlg)
611 m_festProc->ackFinished();
612 return;
614 // Hide the Cancel button so user can't cancel in the middle of playback.
615 m_progressDlg->showCancelButton(false);
616 // Get new wavefile name.
617 m_waveFile = m_festProc->getFilename();
618 // Tell synth we're done.
619 m_festProc->ackFinished();
620 // Play the wave file (possibly adjusting its Speed).
621 // Player object deletes the wave file when done.
622 if (m_player) m_player->play(m_waveFile);
623 QFile::remove(m_waveFile);
624 m_waveFile.clear();
625 if (m_progressDlg) m_progressDlg->close();
628 void FestivalIntConf::slotSynthStopped()
630 // Clean up after canceling test.
631 QString filename = m_festProc->getFilename();
632 // kDebug() << "FestivalIntConf::slotSynthStopped: filename = " << filename << endl;
633 if (!filename.isNull()) QFile::remove(filename);
636 void FestivalIntConf::slotFestivalPath_textChanged()
638 QString exePath = realFilePath(festivalPath->url().path());
639 selectVoiceCombo->setEnabled(false);
640 if (!exePath.isEmpty() && !getLocation(exePath).isEmpty())
642 rescan->setEnabled(true);
643 } else rescan->setEnabled(false);
646 void FestivalIntConf::slotSelectVoiceCombo_activated()
648 int index = selectVoiceCombo->currentIndex();
649 QString codecName = m_voiceList[index].codecName;
650 int codecNdx = PlugInProc::codecNameToListIndex(codecName, m_codecList);
651 characterCodingBox->setCurrentIndex(codecNdx);
652 preloadCheckBox->setChecked(
653 m_voiceList[index].preload);
654 if (m_voiceList[index].volumeAdjustable)
656 volumeBox->setEnabled(true);
657 volumeSlider->setEnabled(true);
659 else
661 volumeBox->setValue(100);
662 volumeBox_valueChanged(100);
663 volumeBox->setEnabled(false);
664 volumeSlider->setEnabled(false);
666 if (m_voiceList[index].rateAdjustable)
668 timeBox->setEnabled(true);
669 timeSlider->setEnabled(true);
671 else
673 timeBox->setValue(100);
674 timeBox_valueChanged(100);
675 timeBox->setEnabled(false);
676 timeSlider->setEnabled(false);
678 if (m_voiceList[index].pitchAdjustable)
680 frequencyBox->setEnabled(true);
681 frequencySlider->setEnabled(true);
683 else
685 frequencyBox->setValue(100);
686 frequencyBox_valueChanged(100);
687 frequencyBox->setEnabled(false);
688 frequencySlider->setEnabled(false);
692 // Basically the slider values are logarithmic (0,...,1000) whereas percent
693 // values are linear (50%,...,200%).
695 // slider = alpha * (log(percent)-log(50))
696 // with alpha = 1000/(log(200)-log(50))
698 int FestivalIntConf::percentToSlider(int percentValue) {
699 double alpha = 1000 / (log(200) - log(50));
700 return (int)floor (0.5 + alpha * (log(percentValue)-log(50)));
703 int FestivalIntConf::sliderToPercent(int sliderValue) {
704 double alpha = 1000 / (log(200) - log(50));
705 return (int)floor(0.5 + exp (sliderValue/alpha + log(50)));
708 void FestivalIntConf::volumeBox_valueChanged(int percentValue) {
709 volumeSlider->setValue(percentToSlider(percentValue));
712 void FestivalIntConf::timeBox_valueChanged(int percentValue) {
713 timeSlider->setValue (percentToSlider (percentValue));
716 void FestivalIntConf::frequencyBox_valueChanged(int percentValue) {
717 frequencySlider->setValue(percentToSlider(percentValue));
720 void FestivalIntConf::volumeSlider_valueChanged(int sliderValue) {
721 volumeBox->setValue(sliderToPercent(sliderValue));
724 void FestivalIntConf::timeSlider_valueChanged(int sliderValue) {
725 timeBox->setValue (sliderToPercent (sliderValue));
728 void FestivalIntConf::frequencySlider_valueChanged(int sliderValue) {
729 frequencyBox->setValue(sliderToPercent(sliderValue));