aboutsummaryrefslogtreecommitdiff
path: root/backends/text-to-speech
diff options
context:
space:
mode:
authorJaromir Wysoglad2019-08-09 00:45:02 +0200
committerFilippos Karapetis2019-09-01 22:47:55 +0300
commitbe5c865c508c93f6910468a07d3947237e7e0a8e (patch)
tree04c6cf60051c2b7436613fd5293d4b7bbb2d9b12 /backends/text-to-speech
parent01d843c527d7808027a546dc5d9a23a981839f8f (diff)
downloadscummvm-rg350-be5c865c508c93f6910468a07d3947237e7e0a8e.tar.gz
scummvm-rg350-be5c865c508c93f6910468a07d3947237e7e0a8e.tar.bz2
scummvm-rg350-be5c865c508c93f6910468a07d3947237e7e0a8e.zip
TTS: Add proper speech queuing, update INT_NO_REP.
Before I used SPD to queue messages and I had a copy of the queue, so I could requeue everything when resume is called(). But more control of the queue is needed, so I don't use the SPD's queue and instead start speeches from my queue one by one from another thread. INTERRUPT_NO_REPEAT now behaves as described in the documentation
Diffstat (limited to 'backends/text-to-speech')
-rw-r--r--backends/text-to-speech/linux/linux-text-to-speech.cpp118
-rw-r--r--backends/text-to-speech/linux/linux-text-to-speech.h17
2 files changed, 102 insertions, 33 deletions
diff --git a/backends/text-to-speech/linux/linux-text-to-speech.cpp b/backends/text-to-speech/linux/linux-text-to-speech.cpp
index cc506db333..bad45e8e88 100644
--- a/backends/text-to-speech/linux/linux-text-to-speech.cpp
+++ b/backends/text-to-speech/linux/linux-text-to-speech.cpp
@@ -33,6 +33,7 @@
#include "common/system.h"
#include "common/ustr.h"
#include "common/config-manager.h"
+#include <pthread.h>
SPDConnection *_connection;
@@ -66,8 +67,32 @@ void speech_pause_callback(size_t msg_id, size_t client_id, SPDNotificationType
manager->updateState(LinuxTextToSpeechManager::SPEECH_PAUSED);
}
+
+void *LinuxTextToSpeechManager::startSpeech(void *p) {
+ StartSpeechParams *params = (StartSpeechParams *) p;
+ pthread_mutex_lock(params->mutex);
+ if (!_connection || g_system->getTextToSpeechManager()->isPaused() ||
+ params->speechQueue->front().empty()) {
+ pthread_mutex_unlock(params->mutex);
+ return NULL;
+ }
+ if(spd_say(_connection, SPD_MESSAGE, params->speechQueue->front().c_str()) == -1) {
+ // close the connection
+ if (_connection != 0) {
+ spd_close(_connection);
+ _connection = 0;
+ }
+ }
+ pthread_mutex_unlock(params->mutex);
+ return NULL;
+}
+
LinuxTextToSpeechManager::LinuxTextToSpeechManager()
: _speechState(READY) {
+ pthread_mutex_init(&_speechMutex, NULL);
+ _params.mutex = &_speechMutex;
+ _params.speechQueue = &_speechQueue;
+ _threadCreated = false;
init();
}
@@ -78,7 +103,6 @@ void LinuxTextToSpeechManager::init() {
warning("Couldn't initialize text to speech through speech-dispatcher");
return;
}
- _lastSaid = "";
_connection->callback_begin = speech_begin_callback;
spd_set_notification_on(_connection, SPD_BEGIN);
@@ -102,8 +126,12 @@ void LinuxTextToSpeechManager::init() {
}
LinuxTextToSpeechManager::~LinuxTextToSpeechManager() {
+ stop();
if (_connection != 0)
spd_close(_connection);
+ if (_threadCreated)
+ pthread_join(_thread, NULL);
+ pthread_mutex_destroy(&_speechMutex);
}
void LinuxTextToSpeechManager::updateState(LinuxTextToSpeechManager::SpeechEvent event) {
@@ -111,9 +139,25 @@ void LinuxTextToSpeechManager::updateState(LinuxTextToSpeechManager::SpeechEvent
return;
switch(event) {
case SPEECH_ENDED:
+ pthread_mutex_lock(&_speechMutex);
_speechQueue.pop_front();
if (_speechQueue.size() == 0)
_speechState = READY;
+ else {
+ // reinitialize if needed
+ if (!_connection)
+ init();
+ if (_speechState != BROKEN) {
+ if (_threadCreated)
+ pthread_join(_thread, NULL);
+ _threadCreated = true;
+ if (pthread_create(&_thread, NULL, startSpeech, &_params)) {
+ _threadCreated = false;
+ warning("TTS: Cannot start new speech");
+ }
+ }
+ }
+ pthread_mutex_unlock(&_speechMutex);
break;
case SPEECH_PAUSED:
_speechState = PAUSED;
@@ -152,18 +196,22 @@ Common::String LinuxTextToSpeechManager::strToUtf8(Common::String str, Common::S
}
bool LinuxTextToSpeechManager::say(Common::String str, Action action, Common::String charset) {
- if (_speechState == BROKEN)
- return true;
- if (action == DROP && isSpeaking())
- return true;
+ pthread_mutex_lock(&_speechMutex);
+ // reinitialize if needed
+ if (!_connection)
+ init();
- if (action == INTERRUPT_NO_REPEAT && _lastSaid == str && isSpeaking())
+ if (_speechState == BROKEN) {
+ pthread_mutex_unlock(&_speechMutex);
return true;
+ }
- if (action == QUEUE_NO_REPEAT && _lastSaid == str && isSpeaking())
+ if (action == DROP && isSpeaking()) {
+ pthread_mutex_unlock(&_speechMutex);
return true;
-
+ }
+
if (charset.empty()) {
#ifdef USE_TRANSLATION
charset = TransMan.getCurrentCharset();
@@ -174,18 +222,30 @@ bool LinuxTextToSpeechManager::say(Common::String str, Action action, Common::St
str = strToUtf8(str, charset);
+ if (!_speechQueue.empty() && action == INTERRUPT_NO_REPEAT &&
+ _speechQueue.front() == str && isSpeaking()) {
+ _speechQueue.clear();
+ _speechQueue.push_back(str);
+ pthread_mutex_unlock(&_speechMutex);
+ return true;
+ }
+
+ if (!_speechQueue.empty() && action == QUEUE_NO_REPEAT &&
+ _speechQueue.back() == str && isSpeaking()) {
+ pthread_mutex_unlock(&_speechMutex);
+ return true;
+ }
+
+ pthread_mutex_unlock(&_speechMutex);
if (isSpeaking() && (action == INTERRUPT || action == INTERRUPT_NO_REPEAT))
stop();
if (!str.empty()) {
- _speechState = SPEAKING;
+ pthread_mutex_lock(&_speechMutex);
_speechQueue.push_back(str);
- _lastSaid = str;
- if(spd_say(_connection, SPD_MESSAGE, str.c_str()) == -1) {
- //restart the connection
- if (_connection != 0)
- spd_close(_connection);
- init();
- return true;
+ pthread_mutex_unlock(&_speechMutex);
+ if (isReady()) {
+ _speechState = SPEAKING;
+ startSpeech((void *)(&_params));
}
}
@@ -196,17 +256,20 @@ bool LinuxTextToSpeechManager::stop() {
if (_speechState == READY || _speechState == BROKEN)
return true;
_speechState = READY;
+ pthread_mutex_lock(&_speechMutex);
_speechQueue.clear();
- return spd_cancel(_connection) == -1;
+ bool result = spd_cancel(_connection) == -1;
+ pthread_mutex_unlock(&_speechMutex);
+ return result;
}
bool LinuxTextToSpeechManager::pause() {
if (_speechState == READY || _speechState == PAUSED || _speechState == BROKEN)
return true;
+ pthread_mutex_lock(&_speechMutex);
_speechState = PAUSED;
bool result = spd_cancel_all(_connection) == -1;
- if (result)
- return true;
+ pthread_mutex_unlock(&_speechMutex);
if (result)
return true;
return false;
@@ -215,16 +278,17 @@ bool LinuxTextToSpeechManager::pause() {
bool LinuxTextToSpeechManager::resume() {
if (_speechState == READY || _speechState == SPEAKING || _speechState == BROKEN)
return true;
+ // If there is a thread from before pause() waiting, let it finish (it shouln't
+ // do anything). There shouldn't be any other threads getting created,
+ // because the speech is paused, so we don't need to synchronize
+ if (_threadCreated) {
+ pthread_join(_thread, NULL);
+ _threadCreated = false;
+ }
+ _speechState = PAUSED;
if (_speechQueue.size()) {
_speechState = SPEAKING;
- for (Common::List<Common::String>::iterator i = _speechQueue.begin(); i != _speechQueue.end(); i++) {
- if (spd_say(_connection, SPD_MESSAGE, i->c_str()) == -1) {
- if (_connection != 0)
- spd_close(_connection);
- init();
- return true;
- }
- }
+ startSpeech((void *) &_params);
}
else
_speechState = READY;
diff --git a/backends/text-to-speech/linux/linux-text-to-speech.h b/backends/text-to-speech/linux/linux-text-to-speech.h
index 23c35c33f6..33f15daccb 100644
--- a/backends/text-to-speech/linux/linux-text-to-speech.h
+++ b/backends/text-to-speech/linux/linux-text-to-speech.h
@@ -30,6 +30,12 @@
#include "common/text-to-speech.h"
#include "common/str.h"
#include "common/list.h"
+#include "common/mutex.h"
+
+struct StartSpeechParams {
+ pthread_mutex_t *mutex;
+ Common::List<Common::String> *speechQueue;
+};
class LinuxTextToSpeechManager : public Common::TextToSpeechManager {
public:
@@ -62,13 +68,9 @@ public:
virtual bool isReady();
virtual void setVoice(unsigned index);
-
virtual void setRate(int rate);
-
virtual void setPitch(int pitch);
-
virtual void setVolume(unsigned volume);
-
virtual void setLanguage(Common::String language);
void updateState(SpeechEvent event);
@@ -80,11 +82,14 @@ private:
virtual void updateVoices();
void createVoice(int typeNumber, Common::TTSVoice::Gender, Common::TTSVoice::Age, char *description);
Common::String strToUtf8(Common::String str, Common::String charset);
- bool spdSay(const char *str);
+ static void *startSpeech(void *p);
+ StartSpeechParams _params;
SpeechState _speechState;
- Common::String _lastSaid;
Common::List<Common::String> _speechQueue;
+ pthread_mutex_t _speechMutex;
+ pthread_t _thread;
+ bool _threadCreated;
};
#endif