1 /***************************************************** vim:set ts=4 sw=4 sts=4:
2 Configuration widget and functions for Festival (Interactive) plug in
5 (C) 2004 by Gary Cramblitt <garycramblitt@comcast.net>
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"
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>
44 #include <kcombobox.h>
46 #include <ktemporaryfile.h>
47 #include <kstandarddirs.h>
48 #include <knuminput.h>
50 #include <kprogressdialog.h>
54 #include "testplayer.h"
57 FestivalIntConf::FestivalIntConf( QWidget
* parent
, const QStringList
& /*args*/) :
58 PlugInConf(parent
, "festivalintconf")
60 // kDebug() << "FestivalIntConf::FestivalIntConf: Running";
63 m_supportsSSML
= FestivalIntProc::ssUnknown
;
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
);
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()));
111 FestivalIntConf::~FestivalIntConf(){
112 // kDebug() << "FestivalIntConf::~FestivalIntConf: Running";
113 if (!m_waveFile
.isNull()) QFile::remove(m_waveFile
);
115 delete m_progressDlg
;
119 * Given a voice code, returns index into m_voiceList array (and voiceCombo box).
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
;
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);
141 QString
voiceSelected(config
.readEntry("Voice"));
142 int index
= voiceCodeToListIndex(voiceSelected
);
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
));
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
)
225 .arg(voiceTemp
.gender
)
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())
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;
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
)
256 index
= currentVoiceIndex
;
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
)
272 // If not found, search for a match on just the language code.
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
)
283 index
= currentVoiceIndex
;
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
)
300 // If not found, pick first voice that is not "Unknown".
303 for(index
= 0 ; index
< m_voiceList
.count(); ++index
)
305 if (m_voiceList
[index
].name
!= i18nc("The voice is unknown", "Unknown"))
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);
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);
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);
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();
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");
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
;
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.
422 m_festProc
->stopText();
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
;
445 m_supportsSSML
= m_festProc
->supportsSSML();
448 if (!m_supportedVoiceCodes
.isEmpty())
450 // User's desktop language setting.
451 QString desktopLanguageCode
= KGlobal::locale()->language();
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
);
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
)
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
)
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
);
505 selectVoiceCombo
->addItem(voiceDisplayName
);
511 voiceStruct voiceTemp
;
512 voiceTemp
.code
= code
;
513 voiceTemp
.name
= i18nc("The voice is unknown", "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.
545 m_festProc
->stopText();
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");
556 QString tmpWaveFile
= tempFile
->fileName();
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
);
571 QTextCodec
* codec
= PlugInProc::codecIndexToCodec(
572 characterCodingBox
->currentIndex(), m_codecList
);
574 // Tell user to wait.
575 m_progressDlg
= new KProgressDialog(this,
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()));
585 realFilePath(festivalPath
->url().path()),
590 frequencyBox
->value(),
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
;
604 void FestivalIntConf::slotSynthFinished()
606 // kDebug() << "FestivalIntConf::slotSynthFinished: Running";
607 // If user canceled, progress dialog is gone, so exit.
610 m_festProc
->ackFinished();
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
);
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);
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);
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);
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
));