diff options
Diffstat (limited to 'src/plugins/tts/flite/qtexttospeech_flite.cpp')
-rwxr-xr-x | src/plugins/tts/flite/qtexttospeech_flite.cpp | 331 |
1 files changed, 32 insertions, 299 deletions
diff --git a/src/plugins/tts/flite/qtexttospeech_flite.cpp b/src/plugins/tts/flite/qtexttospeech_flite.cpp index 86f6974..364727f 100755 --- a/src/plugins/tts/flite/qtexttospeech_flite.cpp +++ b/src/plugins/tts/flite/qtexttospeech_flite.cpp @@ -36,292 +36,19 @@ #include "qtexttospeech_flite.h" -#include <QGlobalStatic> -#include <QIODevice> -#include <QAudioOutput> -#include <QMutex> -#include <QDebug> -#include <QSemaphore> -#include <QThread> - -#include <flite/flite.h> - -// en_US voice: -extern "C" cst_voice *register_cmu_us_kal16(); -extern "C" void unregister_cmu_us_kal16(cst_voice *vox); - QT_BEGIN_NAMESPACE -// Class that handles global Flite initialization and -// creates the processor thread. -class FliteLoader -{ -public: - struct FliteVoiceInfo { - cst_voice *vox; - void (*unregister_func)(cst_voice *vox); - QString name; - QString localeName; - QString gender; - QString age; - }; - FliteLoader() - { - flite_init(); - FliteVoiceInfo voice_enus = { register_cmu_us_kal16(), unregister_cmu_us_kal16, "kal16", "en_US", "male", "adult" }; - if (voice_enus.vox) - m_voices.append(voice_enus); - m_processor = new FliteProcessor(); - QObject::connect(m_processor, &QThread::finished, &QThread::deleteLater); - m_processor->start(); - } - ~FliteLoader() - { - foreach (const FliteLoader::FliteVoiceInfo &voice, m_voices) - voice.unregister_func(voice.vox); - m_processor->exit(); - } - const QVector<FliteVoiceInfo> &voices() const - { - return m_voices; - } - FliteProcessor *processor() const - { - return m_processor; - } -private: - QVector<FliteVoiceInfo> m_voices; - FliteProcessor *m_processor; -}; - -Q_GLOBAL_STATIC(FliteLoader, fliteLoader) - -FliteProcessor::FliteProcessor(): - m_stop(true), - m_idle(true), - m_rate(0), - m_pitch(0), - m_volume(100) -{ -} - -FliteProcessor::~FliteProcessor() -{ -} - -void FliteProcessor::say(cst_voice *voice, const QString &text) -{ - if (isInterruptionRequested()) - return; - QMutexLocker lock(&m_lock); - m_stop = true; // Cancel any previous utterance - m_idle = false; - m_nextText = text; - m_nextVoice = voice; - setRateForVoice(m_nextVoice, m_rate); - setPitchForVoice(m_nextVoice, m_pitch); - m_speakSem.release(); -} - -void FliteProcessor::stop() -{ - QMutexLocker lock(&m_lock); - m_stop = true; - m_nextText.clear(); - m_nextVoice = 0; - m_speakSem.release(); -} - -bool FliteProcessor::setRate(float rate) -{ - QMutexLocker lock(&m_lock); - if (rate >= -1.0 && rate <= 1.0) { - m_rate = rate; - return true; - } - return false; -} - -bool FliteProcessor::setPitch(float pitch) -{ - QMutexLocker lock(&m_lock); - if (pitch >= -1.0 && pitch <= 1.0) { - m_pitch = pitch; - return true; - } - return false; -} - -bool FliteProcessor::setVolume(int volume) -{ - QMutexLocker lock(&m_lock); - if (volume >= 0 && volume <= 100) { - m_volume = volume; - if (m_audio) - m_audio->setVolume(((qreal)m_volume) / 100.0); - return true; - } - return false; -} - -void FliteProcessor::exit() -{ - QThread::exit(); - requestInterruption(); - stop(); -} - -bool FliteProcessor::isIdle() -{ - QMutexLocker lock(&m_lock); - return m_idle; -} - -float FliteProcessor::rate() -{ - QMutexLocker lock(&m_lock); - return m_rate; -} - -float FliteProcessor::pitch() -{ - QMutexLocker lock(&m_lock); - return m_pitch; -} - -int FliteProcessor::volume() -{ - QMutexLocker lock(&m_lock); - return m_volume; -} - -void FliteProcessor::run() -{ - forever { - m_lock.lock(); - if (!m_speakSem.tryAcquire()) { - m_idle = true; - m_lock.unlock(); - emit notSpeaking(); // Going idle - m_speakSem.acquire(); - m_lock.lock(); - } - if (isInterruptionRequested()) { - m_lock.unlock(); - return; - } - m_stop = false; - if (!m_nextText.isEmpty() && m_nextVoice) { - cst_audio_streaming_info *asi; - QString text = m_nextText; - cst_voice *voice = m_nextVoice; - m_nextText.clear(); - m_nextVoice = 0; - m_lock.unlock(); - asi = new_audio_streaming_info(); - asi->asc = FliteProcessor::fliteAudioCb; - asi->userdata = (void *)this; - feat_set(voice->features, "streaming_info", audio_streaming_info_val(asi)); - flite_text_to_speech(text.toUtf8().constData(), voice, "none"); - } else { - m_lock.unlock(); - } - } -} - -void FliteProcessor::setRateForVoice(cst_voice *voice, float rate) -{ - float stretch = 1.0; - Q_ASSERT(rate >= -1.0 && rate <= 1.0); - // Stretch multipliers taken from Speech Dispatcher - if (rate < 0) - stretch -= rate * 2; - if (rate > 0) - stretch -= rate * (100.0 / 175.0); - feat_set_float(voice->features, "duration_stretch", stretch); -} - -void FliteProcessor::setPitchForVoice(cst_voice *voice, float pitch) -{ - float f0; - Q_ASSERT(pitch >= -1.0 && pitch <= 1.0); - // Conversion taken from Speech Dispatcher - f0 = (pitch * 80) + 100; - feat_set_float(voice->features, "int_f0_target_mean", f0); -} - -int FliteProcessor::audioOutput(const cst_wave *w, int start, int size, - int last, cst_audio_streaming_info *asi) -{ - Q_UNUSED(asi); - int ret = CST_AUDIO_STREAM_CONT; - if (start == 0) { - m_lock.lock(); - QAudioFormat format; - format.setSampleRate(w->sample_rate); - format.setChannelCount(w->num_channels); - format.setSampleSize(16); - format.setSampleType(QAudioFormat::SignedInt); - format.setCodec("audio/pcm"); - m_audio = new QAudioOutput(format); - m_audio->setVolume(((qreal)m_volume) / 100.0); - m_audioBuffer = m_audio->start(); - m_lock.unlock(); - } - int bytesToWrite = size * sizeof(short); - int bytesWritten = 0; - forever { - m_lock.lock(); - if (m_stop || !m_audioBuffer - || m_audio->state() == QAudio::StoppedState || isInterruptionRequested()) { - m_lock.unlock(); - ret = CST_AUDIO_STREAM_STOP; - break; - } - bytesWritten += m_audioBuffer->write((const char*)(&w->samples[start + bytesWritten/sizeof(short)]), bytesToWrite - bytesWritten); - m_lock.unlock(); - if (bytesWritten >= bytesToWrite) - break; - QThread::msleep(200); - } - m_lock.lock(); - if (m_stop || last == 1) { - if (m_stop) { - m_audio->reset(); // Discard buffered audio - } else { - // TODO: Find a way to reliably check if all the audio has been written out before stopping - m_audioBuffer->write(QByteArray(1024, 0)); - QThread::msleep(200); - m_audio->stop(); - } - delete m_audio; - m_audio = 0; - m_audioBuffer = 0; - } - m_lock.unlock(); - return ret; -} - -int FliteProcessor::fliteAudioCb(const cst_wave *w, int start, int size, - int last, cst_audio_streaming_info *asi) -{ - FliteProcessor *processor = static_cast<FliteProcessor *>(asi->userdata); - if (processor) - return processor->audioOutput(w, start, size, last, asi); - return CST_AUDIO_STREAM_STOP; -} - QTextToSpeechEngineFlite::QTextToSpeechEngineFlite( const QVariantMap ¶meters, QObject *parent) : QTextToSpeechEngine(parent), - m_state(QTextToSpeech::Ready) + m_state(QTextToSpeech::Ready), + m_processor(QTextToSpeechProcessorFlite::instance()) { Q_UNUSED(parameters); } QTextToSpeechEngineFlite::~QTextToSpeechEngineFlite() { - } QVector<QLocale> QTextToSpeechEngineFlite::availableLocales() const @@ -337,48 +64,54 @@ QVector<QVoice> QTextToSpeechEngineFlite::availableVoices() const void QTextToSpeechEngineFlite::say(const QString &text) { int id = QTextToSpeechEngine::voiceData(m_currentVoice).toInt(); - cst_voice *voiceData = fliteLoader()->voices()[id].vox; m_state = QTextToSpeech::Speaking; emit stateChanged(m_state); - fliteLoader()->processor()->say(voiceData, text); + m_processor->say(text, id); } void QTextToSpeechEngineFlite::stop() { - fliteLoader()->processor()->stop(); + m_processor->stop(); m_state = QTextToSpeech::Ready; emit stateChanged(m_state); } void QTextToSpeechEngineFlite::pause() { - // Not supported, just stop: - stop(); + if (m_state == QTextToSpeech::Speaking) { + m_processor->pause(); + m_state = QTextToSpeech::Paused; + emit stateChanged(m_state); + } } void QTextToSpeechEngineFlite::resume() { - + if (m_state == QTextToSpeech::Paused) { + m_processor->resume(); + m_state = QTextToSpeech::Speaking; + emit stateChanged(m_state); + } } double QTextToSpeechEngineFlite::rate() const { - return fliteLoader()->processor()->rate(); + return m_processor->rate(); } bool QTextToSpeechEngineFlite::setRate(double rate) { - return fliteLoader()->processor()->setRate(rate); + return m_processor->setRate(rate); } double QTextToSpeechEngineFlite::pitch() const { - return fliteLoader()->processor()->pitch(); + return m_processor->pitch(); } bool QTextToSpeechEngineFlite::setPitch(double pitch) { - return fliteLoader()->processor()->setPitch(pitch); + return m_processor->setPitch(pitch); } QLocale QTextToSpeechEngineFlite::locale() const @@ -406,12 +139,12 @@ bool QTextToSpeechEngineFlite::setLocale(const QLocale &locale) int QTextToSpeechEngineFlite::volume() const { - return fliteLoader()->processor()->volume(); + return m_processor->volume(); } bool QTextToSpeechEngineFlite::setVolume(int volume) { - return fliteLoader()->processor()->setVolume(volume); + return m_processor->setVolume(volume); } QVoice QTextToSpeechEngineFlite::voice() const @@ -438,14 +171,13 @@ QTextToSpeech::State QTextToSpeechEngineFlite::state() const bool QTextToSpeechEngineFlite::init(QString *errorString) { int i = 0; - QVector<FliteLoader::FliteVoiceInfo> voices = fliteLoader()->voices(); - foreach (const FliteLoader::FliteVoiceInfo &fliteVoice, voices) { - QVoice::Age age = QVoice::Other; - QVoice::Gender gender = QVoice::Unknown; - QString name = fliteVoice.name; - QLocale locale(fliteVoice.localeName); - QVoice voice = QTextToSpeechEngine::createVoice(name, gender, age, QVariant(i)); - m_voices.insert(fliteVoice.localeName, voice); + const QVector<QTextToSpeechProcessor::VoiceInfo> &voices = m_processor->voices(); + foreach (const QTextToSpeechProcessor::VoiceInfo &voiceInfo, voices) { + QString name = voiceInfo.name; + QLocale locale(voiceInfo.locale); + QVoice voice = QTextToSpeechEngine::createVoice(name, voiceInfo.gender, voiceInfo.age, + QVariant(voiceInfo.id)); + m_voices.insert(voiceInfo.locale, voice); if (!m_locales.contains(locale)) m_locales.append(locale); // Use the first available locale/voice as a fallback @@ -457,16 +189,17 @@ bool QTextToSpeechEngineFlite::init(QString *errorString) } // Attempt to switch to the system locale setLocale(QLocale::system()); - connect(fliteLoader()->processor(), &FliteProcessor::notSpeaking, + connect(m_processor.data(), &QTextToSpeechProcessor::notSpeaking, this, &QTextToSpeechEngineFlite::onNotSpeaking); if (errorString) - *errorString = QStringLiteral(""); + *errorString = QString(); return true; } -void QTextToSpeechEngineFlite::onNotSpeaking() +void QTextToSpeechEngineFlite::onNotSpeaking(int statusCode) { - if (m_state != QTextToSpeech::Ready && fliteLoader()->processor()->isIdle()) { + Q_UNUSED(statusCode); + if (m_state != QTextToSpeech::Ready && m_processor->isIdle()) { m_state = QTextToSpeech::Ready; emit stateChanged(m_state); } |