[Scummvm-git-logs] scummvm master -> 64c811b44bfe1aac99993e89f3fd3906d1d325dd

criezy criezy at scummvm.org
Mon Jun 29 21:49:12 UTC 2020


This automated email contains information about 14 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
b7bd904bc0 GLK: Add missing clear for debug channels when leaving the engine
dd38dcbd8a GLK: Add speech debug channel
92769bb14b GLK: Add calls to initialize and free tts in the TextBufferWindow
56f1049c21 GLK: Add initial implementation of TTS
ceb5299811 GLK: Add engine options to enable TTS
1210aecab2 GLK: Use speech volume from options to control the TTS volume
31ea00f465 GLK: Try to properly handle speech with multiple TextBufferWindows
5f34aaa52b ENGINES: Pop the TTS stack before calling syncSoundSettings
98bc3f45bd GLK: Add speech volume synchronization
8f3c2e5754 GLK: Simplify SpeechManager
cbf6b6b6e6 GLK: Add tts flush when redrawing the text buffer window
01f3ce620a GLK: Add method that takes c-string text in Speech class
f35f0f2804 GLK: Implement TTS for the TextGridWindow
64c811b44b GLK: Do not interrupt current speech when getting text from a different window


Commit: b7bd904bc00e3f57eb6a754c15265df0ff28af3b
    https://github.com/scummvm/scummvm/commit/b7bd904bc00e3f57eb6a754c15265df0ff28af3b
Author: Thierry Crozat (criezy at scummvm.org)
Date: 2020-06-29T22:48:57+01:00

Commit Message:
GLK: Add missing clear for debug channels when leaving the engine

Changed paths:
    engines/glk/glk.cpp


diff --git a/engines/glk/glk.cpp b/engines/glk/glk.cpp
index c6bb80edf5..ff12276365 100644
--- a/engines/glk/glk.cpp
+++ b/engines/glk/glk.cpp
@@ -73,6 +73,9 @@ GlkEngine::~GlkEngine() {
 	delete _sounds;
 	delete _streams;
 	delete _windows;
+
+	// Remove all of our debug levels here
+	DebugMan.clearAllDebugChannels();
 }
 
 void GlkEngine::initialize() {


Commit: dd38dcbd8a825c303fd0e61ad2fba6c513dd6740
    https://github.com/scummvm/scummvm/commit/dd38dcbd8a825c303fd0e61ad2fba6c513dd6740
Author: Thierry Crozat (criezy at scummvm.org)
Date: 2020-06-29T22:48:57+01:00

Commit Message:
GLK: Add speech debug channel

Changed paths:
    engines/glk/glk.cpp
    engines/glk/glk.h


diff --git a/engines/glk/glk.cpp b/engines/glk/glk.cpp
index ff12276365..f2a796191a 100644
--- a/engines/glk/glk.cpp
+++ b/engines/glk/glk.cpp
@@ -57,6 +57,7 @@ GlkEngine::GlkEngine(OSystem *syst, const GlkGameDescription &gameDesc) :
 	DebugMan.addDebugChannel(kDebugScripts, "scripts", "Game scripts");
 	DebugMan.addDebugChannel(kDebugGraphics, "graphics", "Graphics handling");
 	DebugMan.addDebugChannel(kDebugSound, "sound", "Sound and Music handling");
+	DebugMan.addDebugChannel(kDebugSpeech, "speech", "Text to Speech handling");
 
 	g_vm = this;
 }
diff --git a/engines/glk/glk.h b/engines/glk/glk.h
index 9a3bf1b4cc..39363a80d8 100644
--- a/engines/glk/glk.h
+++ b/engines/glk/glk.h
@@ -50,7 +50,8 @@ enum GlkDebugChannels {
 	kDebugCore      = 1 << 0,
 	kDebugScripts   = 1 << 1,
 	kDebugGraphics  = 1 << 2,
-	kDebugSound     = 1 << 3
+	kDebugSound     = 1 << 3,
+	kDebugSpeech    = 1 << 4
 };
 
 


Commit: 92769bb14b7a663502c7e4afba52c572e20dddf7
    https://github.com/scummvm/scummvm/commit/92769bb14b7a663502c7e4afba52c572e20dddf7
Author: Thierry Crozat (criezy at scummvm.org)
Date: 2020-06-29T22:48:57+01:00

Commit Message:
GLK: Add calls to initialize and free tts in the TextBufferWindow

Changed paths:
    engines/glk/window_text_buffer.cpp


diff --git a/engines/glk/window_text_buffer.cpp b/engines/glk/window_text_buffer.cpp
index 6a8bb95195..c1e4ec31b3 100644
--- a/engines/glk/window_text_buffer.cpp
+++ b/engines/glk/window_text_buffer.cpp
@@ -50,9 +50,11 @@ TextBufferWindow::TextBufferWindow(Windows *windows, uint rock) : TextWindow(win
 	_attrs = _lines[0]._attrs;
 
 	Common::copy(&g_conf->_tStyles[0], &g_conf->_tStyles[style_NUMSTYLES], _styles);
+	gli_initialize_tts();
 }
 
 TextBufferWindow::~TextBufferWindow() {
+	gli_free_tts();
 	if (_inBuf) {
 		if (g_vm->gli_unregister_arr)
 			(*g_vm->gli_unregister_arr)(_inBuf, _inMax, "&+#!Cn", _inArrayRock);


Commit: 56f1049c21125e710cd85eb6276e8b8b410b3fdf
    https://github.com/scummvm/scummvm/commit/56f1049c21125e710cd85eb6276e8b8b410b3fdf
Author: Thierry Crozat (criezy at scummvm.org)
Date: 2020-06-29T22:48:57+01:00

Commit Message:
GLK: Add initial implementation of TTS

Changed paths:
  A engines/glk/speech.cpp
    engines/glk/module.mk
    engines/glk/speech.h


diff --git a/engines/glk/module.mk b/engines/glk/module.mk
index 6c9871617d..c30b0bfb05 100644
--- a/engines/glk/module.mk
+++ b/engines/glk/module.mk
@@ -17,6 +17,7 @@ MODULE_OBJS := \
 	screen.o \
 	selection.o \
 	sound.o \
+	speech.o \
 	streams.o \
 	time.o \
 	unicode.o \
diff --git a/engines/glk/speech.cpp b/engines/glk/speech.cpp
new file mode 100644
index 0000000000..11858ef2f2
--- /dev/null
+++ b/engines/glk/speech.cpp
@@ -0,0 +1,116 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software{} you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation{} either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY{} without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program{} if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "glk/speech.h"
+
+#if defined(USE_TTS)
+#include "common/config-manager.h"
+#include "common/system.h"
+#include "common/debug.h"
+#include "glk/glk.h"
+#endif
+
+namespace Glk {
+
+void Speech::gli_initialize_tts(void) {
+#if defined(USE_TTS)
+	debugC(kDebugSpeech, "gli_initialize_tts()");
+	if (_ttsMan != nullptr)
+		return;
+	_ttsMan = g_system->getTextToSpeechManager();
+	if (_ttsMan != nullptr) {
+		_ttsMan->pushState();
+		// Language
+		_ttsMan->setLanguage(ConfMan.get("language"));
+		// Volume
+		_ttsMan->setVolume(75);
+		// Voice
+		unsigned voice;
+		if(ConfMan.hasKey("tts_voice")) {
+			voice = ConfMan.getInt("tts_voice");
+			if (voice >= _ttsMan->getVoicesArray().size())
+				voice = _ttsMan->getDefaultVoice();
+		} else
+			voice = _ttsMan->getDefaultVoice();
+		_ttsMan->setVoice(voice);
+	}
+#endif
+}
+
+void Speech::gli_tts_flush(void) {
+#if defined(USE_TTS)
+	debugC(kDebugSpeech, "gli_tts_flush()");
+	if (_ttsMan != nullptr && !_speechBuffer.empty()) {
+		// Curently the TextToSpeechManager takes a String, which does not properly support
+		// UTF-32. So convert to UTF-8.
+		Common::String text = _speechBuffer.encode();
+		debugC(kDebugSpeech, "Speaking: \"%s\"", text.c_str());
+		_ttsMan->say(text, Common::TextToSpeechManager::QUEUE, "utf-8");
+		_speechBuffer.clear();
+	}
+#endif
+}
+
+void Speech::gli_tts_purge(void) {
+#if defined(USE_TTS)
+	debugC(kDebugSpeech, "gli_tts_purge()");
+	if (_ttsMan != nullptr) {
+		_speechBuffer.clear();
+		_ttsMan->stop();
+	}
+#endif
+}
+
+void Speech::gli_tts_speak(const uint32 *buf, size_t len) {
+#if defined(USE_TTS)
+	debugC(1, kDebugSpeech, "gli_tts_speak()");
+	if (_ttsMan != nullptr) {
+		for (int i = 0 ; i < len ; ++i, ++buf) {
+			// Should we automatically flush on new lines without waiting for the call to gli_tts_flush?
+			// Should we also flush on '.', '?', and '!'?
+//			if (*buf == '\n') {
+//				debugC(1, kDebugSpeech, "flush speech buffer on new line");
+//				gli_tts_flush();
+//			} else {
+				_speechBuffer += *buf;
+				debugC(1, kDebugSpeech, "speech buffer: %s", _speechBuffer.encode().c_str());
+//			}
+		}
+	}
+#endif
+}
+
+void Speech::gli_free_tts(void) {
+#if defined(USE_TTS)
+	debugC(kDebugSpeech, "gli_free_tts()");
+	if (_ttsMan != nullptr) {
+		_ttsMan->popState();
+		_ttsMan = nullptr;
+	}
+#endif
+}
+
+} // End of namespace Glk
+
+
+
+
diff --git a/engines/glk/speech.h b/engines/glk/speech.h
index 273c953fed..3a2ed9daa4 100644
--- a/engines/glk/speech.h
+++ b/engines/glk/speech.h
@@ -26,22 +26,38 @@
 #include "common/events.h"
 #include "glk/glk_types.h"
 
+#if defined(USE_TTS)
+#include "common/text-to-speech.h"
+#include "common/ustr.h"
+#endif
+
 namespace Glk {
 
 /**
  * Currently not implemented
  */
 class Speech {
+private:
+#if defined(USE_TTS)
+	Common::U32String _speechBuffer;
+	Common::TextToSpeechManager *_ttsMan;
+#endif
+
 protected:
-	void gli_initialize_tts(void) {}
+#if defined(USE_TTS)
+	Speech() : _ttsMan(nullptr) {}
+	~Speech() { if (_ttsMan) _ttsMan->popState(); }
+#endif
+
+	void gli_initialize_tts(void);
 
-	void gli_tts_flush(void) {}
+	void gli_tts_flush(void);
 
-	void gli_tts_purge(void) {}
+	void gli_tts_purge(void);
 
-	void gli_tts_speak(const uint32 *buf, size_t len) {}
+	void gli_tts_speak(const uint32 *buf, size_t len);
 
-	void gli_free_tts(void) {}
+	void gli_free_tts(void);
 };
 
 } // End of namespace Glk


Commit: ceb5299811200705ceb553b2b3439e7bba77b1d3
    https://github.com/scummvm/scummvm/commit/ceb5299811200705ceb553b2b3439e7bba77b1d3
Author: Thierry Crozat (criezy at scummvm.org)
Date: 2020-06-29T22:48:57+01:00

Commit Message:
GLK: Add engine options to enable TTS

Changed paths:
    engines/glk/POTFILES
    engines/glk/conf.cpp
    engines/glk/detection.cpp
    engines/glk/detection.h
    engines/glk/window_text_buffer.cpp


diff --git a/engines/glk/POTFILES b/engines/glk/POTFILES
index f21c2c9bd5..ef29a25887 100644
--- a/engines/glk/POTFILES
+++ b/engines/glk/POTFILES
@@ -1,3 +1,4 @@
+engines/glk/detection.cpp
 engines/glk/glk_api.cpp
 engines/glk/quetzal.cpp
 engines/glk/streams.cpp
diff --git a/engines/glk/conf.cpp b/engines/glk/conf.cpp
index 8c33a8a7db..fbe9019c91 100644
--- a/engines/glk/conf.cpp
+++ b/engines/glk/conf.cpp
@@ -216,7 +216,8 @@ void Conf::get(const Common::String &key, int &field, int defaultVal) {
 }
 
 void Conf::get(const Common::String &key, bool &field, bool defaultVal) {
-	field = ConfMan.hasKey(key) ? strToInt(ConfMan.get(key).c_str()) != 0 : defaultVal;
+	if (!ConfMan.hasKey(key) || !Common::parseBool(ConfMan.get(key), field))
+		field = defaultVal;
 }
 
 void Conf::get(const Common::String &key, FACES &field, FACES defaultFont) {
diff --git a/engines/glk/detection.cpp b/engines/glk/detection.cpp
index 4b9024afe9..7983a79bd6 100644
--- a/engines/glk/detection.cpp
+++ b/engines/glk/detection.cpp
@@ -66,6 +66,7 @@
 #include "graphics/surface.h"
 #include "common/config-manager.h"
 #include "common/file.h"
+#include "common/translation.h"
 
 namespace Glk {
 
@@ -338,6 +339,27 @@ void GlkMetaEngine::detectClashes() const {
 	Glk::ZCode::ZCodeMetaEngine::detectClashes(map);
 }
 
+const ExtraGuiOptions GlkMetaEngine::getExtraGuiOptions(const Common::String &) const {
+	ExtraGuiOptions  options;
+#if defined(USE_TTS)
+	static const ExtraGuiOption ttsSpeakOptions = {
+		_s("Enable Text to Speech"),
+		_s("Use TTS to read the text"),
+		"speak",
+		false
+	};
+	static const ExtraGuiOption ttsSpeakInputOptions = {
+		_s("Also read input text"),
+		_s("Use TTS to read the input text"),
+		"speak_input",
+		false
+	};
+	options.push_back(ttsSpeakOptions);
+	options.push_back(ttsSpeakInputOptions);
+#endif
+	return options;
+}
+
 SaveStateList GlkMetaEngine::listSaves(const char *target) const {
 	Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
 	Common::StringArray filenames;
diff --git a/engines/glk/detection.h b/engines/glk/detection.h
index d61b1a6639..b538245cf7 100644
--- a/engines/glk/detection.h
+++ b/engines/glk/detection.h
@@ -77,6 +77,11 @@ public:
 	 * Calls each sub-engine in turn to ensure no game Id accidentally shares the same Id
 	 */
 	void detectClashes() const;
+
+	/**
+	 * Return a list of extra GUI options for the specified target.
+	 */
+	const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const override;
 };
 
 namespace Glk {
diff --git a/engines/glk/window_text_buffer.cpp b/engines/glk/window_text_buffer.cpp
index c1e4ec31b3..d212d8f783 100644
--- a/engines/glk/window_text_buffer.cpp
+++ b/engines/glk/window_text_buffer.cpp
@@ -50,11 +50,15 @@ TextBufferWindow::TextBufferWindow(Windows *windows, uint rock) : TextWindow(win
 	_attrs = _lines[0]._attrs;
 
 	Common::copy(&g_conf->_tStyles[0], &g_conf->_tStyles[style_NUMSTYLES], _styles);
-	gli_initialize_tts();
+
+	if (g_conf->_speak)
+		gli_initialize_tts();
 }
 
 TextBufferWindow::~TextBufferWindow() {
-	gli_free_tts();
+	if (g_conf->_speak)
+		gli_free_tts();
+
 	if (_inBuf) {
 		if (g_vm->gli_unregister_arr)
 			(*g_vm->gli_unregister_arr)(_inBuf, _inMax, "&+#!Cn", _inArrayRock);


Commit: 1210aecab2dc39e0916bdfd7ea9cc53b215e4293
    https://github.com/scummvm/scummvm/commit/1210aecab2dc39e0916bdfd7ea9cc53b215e4293
Author: Thierry Crozat (criezy at scummvm.org)
Date: 2020-06-29T22:48:57+01:00

Commit Message:
GLK: Use speech volume from options to control the TTS volume

Changed paths:
    engines/glk/detection.cpp
    engines/glk/detection.h
    engines/glk/speech.cpp
    engines/glk/zcode/detection_tables.h


diff --git a/engines/glk/detection.cpp b/engines/glk/detection.cpp
index 7983a79bd6..4c045f423b 100644
--- a/engines/glk/detection.cpp
+++ b/engines/glk/detection.cpp
@@ -70,17 +70,25 @@
 
 namespace Glk {
 
+Common::String GlkDetectedGame::getGlkGUIOptions() {
+#if defined (USE_TTS)
+	return GUIO2(GUIO_NOMUSIC, GUIO_NOSUBTITLES);
+#else
+	return GUIO3(GUIO_NOSPEECH, GUIO_NOMUSIC, GUIO_NOSUBTITLES);
+#endif
+}
+
 GlkDetectedGame::GlkDetectedGame(const char *id, const char *desc, const Common::String &filename,
 		GameSupportLevel supportLevel) :
 		DetectedGame("glk", id, desc, Common::EN_ANY, Common::kPlatformUnknown) {
-	setGUIOptions(GUIO3(GUIO_NOSPEECH, GUIO_NOMUSIC, GUIO_NOSUBTITLES));
+	setGUIOptions(getGlkGUIOptions());
 	gameSupportLevel = supportLevel;
 	addExtraEntry("filename", filename);
 }
 
 GlkDetectedGame::GlkDetectedGame(const char *id, const char *desc, const Common::String &filename,
 		Common::Language lang, GameSupportLevel supportLevel) : DetectedGame("glk", id, desc, lang, Common::kPlatformUnknown) {
-	setGUIOptions(GUIO3(GUIO_NOSPEECH, GUIO_NOMUSIC, GUIO_NOSUBTITLES));
+	setGUIOptions(getGlkGUIOptions());
 	gameSupportLevel = supportLevel;
 	addExtraEntry("filename", filename);
 }
@@ -89,7 +97,7 @@ GlkDetectedGame::GlkDetectedGame(const char *id, const char *desc, const char *x
 		const Common::String &filename, Common::Language lang,
 		GameSupportLevel supportLevel) :
 		DetectedGame("glk", id, desc, lang, Common::kPlatformUnknown, xtra) {
-	setGUIOptions(GUIO3(GUIO_NOSPEECH, GUIO_NOMUSIC, GUIO_NOSUBTITLES));
+	setGUIOptions(getGlkGUIOptions());
 	gameSupportLevel = supportLevel;
 	addExtraEntry("filename", filename);
 }
@@ -97,7 +105,7 @@ GlkDetectedGame::GlkDetectedGame(const char *id, const char *desc, const char *x
 GlkDetectedGame::GlkDetectedGame(const char *id, const char *desc, const Common::String &filename,
 		const Common::String &md5, size_t filesize, GameSupportLevel supportLevel) :
 		DetectedGame("glk", id, desc, Common::UNK_LANG, Common::kPlatformUnknown) {
-	setGUIOptions(GUIO3(GUIO_NOSPEECH, GUIO_NOMUSIC, GUIO_NOSUBTITLES));
+	setGUIOptions(getGlkGUIOptions());
 	gameSupportLevel = supportLevel;
 	addExtraEntry("filename", filename);
 
diff --git a/engines/glk/detection.h b/engines/glk/detection.h
index b538245cf7..d7db11dae0 100644
--- a/engines/glk/detection.h
+++ b/engines/glk/detection.h
@@ -127,6 +127,8 @@ public:
 	GlkDetectedGame(const char *id, const char *desc, const char *extra,
 		const Common::String &filename, Common::Language lang,
 		GameSupportLevel supportLevel = kTestingGame);
+
+	static Common::String getGlkGUIOptions();
 };
 
 /**
diff --git a/engines/glk/speech.cpp b/engines/glk/speech.cpp
index 11858ef2f2..211722c9a0 100644
--- a/engines/glk/speech.cpp
+++ b/engines/glk/speech.cpp
@@ -42,7 +42,10 @@ void Speech::gli_initialize_tts(void) {
 		// Language
 		_ttsMan->setLanguage(ConfMan.get("language"));
 		// Volume
-		_ttsMan->setVolume(75);
+		int volume = (ConfMan.getInt("speech_volume") * 100) / 256;
+		if (ConfMan.hasKey("mute", "scummvm") && ConfMan.getBool("mute", "scummvm"))
+			volume = 0;
+		_ttsMan->setVolume(volume);
 		// Voice
 		unsigned voice;
 		if(ConfMan.hasKey("tts_voice")) {
diff --git a/engines/glk/zcode/detection_tables.h b/engines/glk/zcode/detection_tables.h
index bb3a373319..b558a867ef 100644
--- a/engines/glk/zcode/detection_tables.h
+++ b/engines/glk/zcode/detection_tables.h
@@ -996,10 +996,15 @@ const PlainGameDescriptor ZCODE_GAME_LIST[] = {
 	{ nullptr, nullptr }
 };
 
+#if defined (USE_TTS)
+#define NONE GUIO3(GUIO_NOSFX, GUIO_NOMUSIC, GUIO_NOSUBTITLES)
+#define ENTRYS(ID, VERSION, MD5, FILESIZE) { ID, VERSION, MD5, FILESIZE, Common::EN_ANY, GUIO2(GUIO_NOMUSIC, GUIO_NOSUBTITLES) }
+#else
 #define NONE GUIO4(GUIO_NOSPEECH, GUIO_NOSFX, GUIO_NOMUSIC, GUIO_NOSUBTITLES)
+#define ENTRYS(ID, VERSION, MD5, FILESIZE) { ID, VERSION, MD5, FILESIZE, Common::EN_ANY, GUIO3(GUIO_NOSPEECH, GUIO_NOMUSIC, GUIO_NOSUBTITLES) }
+#endif
 #define ENTRY0(ID, VERSION, MD5, FILESIZE) { ID, VERSION, MD5, FILESIZE, Common::EN_ANY, NONE }
 #define ENTRY1(ID, VERSION, MD5, FILESIZE, LANG) { ID, VERSION, MD5, FILESIZE, LANG, NONE }
-#define ENTRYS(ID, VERSION, MD5, FILESIZE) { ID, VERSION, MD5, FILESIZE, Common::EN_ANY, GUIO3(GUIO_NOSPEECH, GUIO_NOMUSIC, GUIO_NOSUBTITLES) }
 
 #define FROTZ_TABLE_END_MARKER { nullptr, nullptr, nullptr, 0, Common::EN_ANY, "" }
 


Commit: 31ea00f46542a1dfdb8fee0d4ad2f40f45fbee14
    https://github.com/scummvm/scummvm/commit/31ea00f46542a1dfdb8fee0d4ad2f40f45fbee14
Author: Thierry Crozat (criezy at scummvm.org)
Date: 2020-06-29T22:48:57+01:00

Commit Message:
GLK: Try to properly handle speech with multiple TextBufferWindows

Changed paths:
    engines/glk/speech.cpp
    engines/glk/speech.h


diff --git a/engines/glk/speech.cpp b/engines/glk/speech.cpp
index 211722c9a0..c812a973b1 100644
--- a/engines/glk/speech.cpp
+++ b/engines/glk/speech.cpp
@@ -21,19 +21,44 @@
  */
 
 #include "glk/speech.h"
+#include "common/debug.h"
+#include "glk/glk.h"
 
 #if defined(USE_TTS)
 #include "common/config-manager.h"
 #include "common/system.h"
-#include "common/debug.h"
-#include "glk/glk.h"
 #endif
 
 namespace Glk {
 
-void Speech::gli_initialize_tts(void) {
+SpeechManager *SpeechManager::_instance = nullptr;
+
+SpeechManager* SpeechManager::getSpeechManagerInstance() {
+#if defined(USE_TTS)
+	if (!_instance)
+		_instance = new SpeechManager();
+	++_instance->_refCount;
+	return _instance;
+#else
+	return nullptr;
+#endif
+}
+
+void SpeechManager::releaseSpeechManagerInstance() {
+	if (--_refCount == 0) {
+		_instance = nullptr;
+		delete this;
+	}
+}
+
+SpeechManager::SpeechManager() :
+	_refCount(0)
 #if defined(USE_TTS)
-	debugC(kDebugSpeech, "gli_initialize_tts()");
+	, _ttsMan(nullptr), _lastSpeechSource(nullptr), _nextSpeechAction(Common::TextToSpeechManager::QUEUE)
+#endif
+{
+#if defined(USE_TTS)
+	debugC(kDebugSpeech, "Initialize Glk::SpeechManager");
 	if (_ttsMan != nullptr)
 		return;
 	_ttsMan = g_system->getTextToSpeechManager();
@@ -55,61 +80,122 @@ void Speech::gli_initialize_tts(void) {
 		} else
 			voice = _ttsMan->getDefaultVoice();
 		_ttsMan->setVoice(voice);
+	} else
+		debugC(kDebugSpeech, "Text to Speech is not available");
+#endif
+}
+
+SpeechManager::~SpeechManager() {
+#if defined(USE_TTS)
+	debugC(kDebugSpeech, "Destroy Glk::SpeechManager");
+	if (_ttsMan != nullptr) {
+		_ttsMan->popState();
+		_ttsMan = nullptr;
 	}
 #endif
 }
 
-void Speech::gli_tts_flush(void) {
+void SpeechManager::flushSpeech(Speech * source) {
 #if defined(USE_TTS)
-	debugC(kDebugSpeech, "gli_tts_flush()");
-	if (_ttsMan != nullptr && !_speechBuffer.empty()) {
-		// Curently the TextToSpeechManager takes a String, which does not properly support
-		// UTF-32. So convert to UTF-8.
-		Common::String text = _speechBuffer.encode();
-		debugC(kDebugSpeech, "Speaking: \"%s\"", text.c_str());
-		_ttsMan->say(text, Common::TextToSpeechManager::QUEUE, "utf-8");
-		_speechBuffer.clear();
+	if (source != _lastSpeechSource) {
+		debugC(kDebugSpeech, "SpeechManager::flushSpeech() called with a different source than the last text source");
+		purgeSpeech(source);
+	} else {
+		if (_ttsMan != nullptr && !_speechBuffer.empty()) {
+			// Curently the TextToSpeechManager takes a String, which does not properly support
+			// UTF-32. So convert to UTF-8.
+			Common::String text = _speechBuffer.encode();
+			debugC(kDebugSpeech, "Speaking: \"%s\"", text.c_str());
+			_ttsMan->say(text, _nextSpeechAction, "utf-8");
+			_speechBuffer.clear();
+			_nextSpeechAction = Common::TextToSpeechManager::QUEUE;
+		}
 	}
 #endif
 }
 
-void Speech::gli_tts_purge(void) {
+void SpeechManager::purgeSpeech(Speech *source) {
 #if defined(USE_TTS)
-	debugC(kDebugSpeech, "gli_tts_purge()");
+	debugC(kDebugSpeech, "SpeechManager::purgeSpeech()");
 	if (_ttsMan != nullptr) {
 		_speechBuffer.clear();
 		_ttsMan->stop();
 	}
+	_nextSpeechAction = Common::TextToSpeechManager::QUEUE;
+	_lastSpeechSource = source;
 #endif
 }
 
-void Speech::gli_tts_speak(const uint32 *buf, size_t len) {
+void SpeechManager::addSpeech(const uint32 *buf, size_t len, Speech *source) {
 #if defined(USE_TTS)
-	debugC(1, kDebugSpeech, "gli_tts_speak()");
+	if (source != _lastSpeechSource) {
+		debugC(kDebugSpeech, "Flushing SpeechManager buffer for a different speech source");
+		if (!_speechBuffer.empty()) {
+			// Flush the pending speech, but allow interupting it if we flush the text
+			// for the new source before it has finished.
+			flushSpeech(_lastSpeechSource);
+			_nextSpeechAction = Common::TextToSpeechManager::INTERRUPT;
+		}
+		_lastSpeechSource = source;
+	}
 	if (_ttsMan != nullptr) {
+		debugC(1, kDebugSpeech, "SpeechManager add speech");
 		for (int i = 0 ; i < len ; ++i, ++buf) {
 			// Should we automatically flush on new lines without waiting for the call to gli_tts_flush?
 			// Should we also flush on '.', '?', and '!'?
-//			if (*buf == '\n') {
-//				debugC(1, kDebugSpeech, "flush speech buffer on new line");
-//				gli_tts_flush();
-//			} else {
-				_speechBuffer += *buf;
-				debugC(1, kDebugSpeech, "speech buffer: %s", _speechBuffer.encode().c_str());
-//			}
+			//if (*buf == '\n') {
+			//	debugC(1, kDebugSpeech, "Flushing SpeechManager buffer on new line");
+			//	gli_tts_flush();
+			//} else {
+			_speechBuffer += *buf;
+			//}
 		}
+//		debugC(1, kDebugSpeech, "SpeechManager buffer: %s", _speechBuffer.encode().c_str());
 	}
 #endif
 }
 
+
+Speech::Speech() : _speechManager(nullptr) {
+}
+
+Speech::~Speech() {
+	if (_speechManager) {
+		warning("Unbalanced calls to gli_initialize_tts and gli_free_tts");
+		_speechManager->releaseSpeechManagerInstance();
+	}
+}
+
+void Speech::gli_initialize_tts(void) {
+	debugC(kDebugSpeech, "gli_initialize_tts");
+	if (!_speechManager)
+		_speechManager = SpeechManager::getSpeechManagerInstance();
+}
+
+void Speech::gli_tts_flush(void) {
+	debugC(kDebugSpeech, "gli_tts_flush");
+	if (_speechManager)
+		_speechManager->flushSpeech(this);
+}
+
+void Speech::gli_tts_purge(void) {
+	debugC(kDebugSpeech, "gli_tts_purge");
+	if (_speechManager)
+		_speechManager->purgeSpeech(this);
+}
+
+void Speech::gli_tts_speak(const uint32 *buf, size_t len) {
+	debugC(1, kDebugSpeech, "gli_tts_speak");
+	if (_speechManager)
+		_speechManager->addSpeech(buf, len, this);
+}
+
 void Speech::gli_free_tts(void) {
-#if defined(USE_TTS)
-	debugC(kDebugSpeech, "gli_free_tts()");
-	if (_ttsMan != nullptr) {
-		_ttsMan->popState();
-		_ttsMan = nullptr;
+	debugC(kDebugSpeech, "gli_free_tts");
+	if (_speechManager) {
+		_speechManager->releaseSpeechManagerInstance();
+		_speechManager = nullptr;
 	}
-#endif
 }
 
 } // End of namespace Glk
diff --git a/engines/glk/speech.h b/engines/glk/speech.h
index 3a2ed9daa4..5dfd62809e 100644
--- a/engines/glk/speech.h
+++ b/engines/glk/speech.h
@@ -25,6 +25,7 @@
 
 #include "common/events.h"
 #include "glk/glk_types.h"
+#include "common/singleton.h"
 
 #if defined(USE_TTS)
 #include "common/text-to-speech.h"
@@ -33,21 +34,40 @@
 
 namespace Glk {
 
-/**
- * Currently not implemented
- */
-class Speech {
+class Speech;
+
+class SpeechManager {
+public:
+	static SpeechManager* getSpeechManagerInstance();
+	void releaseSpeechManagerInstance();
+
+	void flushSpeech(Speech *);
+	void purgeSpeech(Speech *);
+	void addSpeech(const uint32 *buf, size_t len, Speech *);
+
 private:
+	SpeechManager();
+	~SpeechManager();
+
+	static SpeechManager *_instance;
+	int _refCount;
+
 #if defined(USE_TTS)
 	Common::U32String _speechBuffer;
 	Common::TextToSpeechManager *_ttsMan;
+	Common::TextToSpeechManager::Action _nextSpeechAction;
+	Speech *_lastSpeechSource;
 #endif
+};
+
+
+class Speech {
+private:
+	SpeechManager *_speechManager;
 
 protected:
-#if defined(USE_TTS)
-	Speech() : _ttsMan(nullptr) {}
-	~Speech() { if (_ttsMan) _ttsMan->popState(); }
-#endif
+	Speech();
+	~Speech();
 
 	void gli_initialize_tts(void);
 


Commit: 5f34aaa52b275415d7dee895a95809dd7b273b34
    https://github.com/scummvm/scummvm/commit/5f34aaa52b275415d7dee895a95809dd7b273b34
Author: Thierry Crozat (criezy at scummvm.org)
Date: 2020-06-29T22:48:57+01:00

Commit Message:
ENGINES: Pop the TTS stack before calling syncSoundSettings

This allows engine to change the TTS speech volume for the correct
state if they use the Options  text volume for TTS.

Changed paths:
    engines/engine.cpp


diff --git a/engines/engine.cpp b/engines/engine.cpp
index 658bc47866..be24f0b83a 100644
--- a/engines/engine.cpp
+++ b/engines/engine.cpp
@@ -589,12 +589,13 @@ void Engine::openMainMenuDialog() {
 		}
 	}
 
-	applyGameSettings();
-	syncSoundSettings();
 #ifdef USE_TTS
 	if (ttsMan != nullptr)
 		ttsMan->popState();
 #endif
+
+	applyGameSettings();
+	syncSoundSettings();
 }
 
 bool Engine::warnUserAboutUnsupportedGame() {


Commit: 98bc3f45bd7904a4e698d2e0c8a0ff4981dfa047
    https://github.com/scummvm/scummvm/commit/98bc3f45bd7904a4e698d2e0c8a0ff4981dfa047
Author: Thierry Crozat (criezy at scummvm.org)
Date: 2020-06-29T22:48:57+01:00

Commit Message:
GLK: Add speech volume synchronization

Changed paths:
    engines/glk/glk.cpp
    engines/glk/speech.cpp
    engines/glk/speech.h


diff --git a/engines/glk/glk.cpp b/engines/glk/glk.cpp
index f2a796191a..7acf4d3a6a 100644
--- a/engines/glk/glk.cpp
+++ b/engines/glk/glk.cpp
@@ -38,6 +38,7 @@
 #include "glk/screen.h"
 #include "glk/selection.h"
 #include "glk/sound.h"
+#include "glk/speech.h"
 #include "glk/streams.h"
 #include "glk/windows.h"
 
@@ -271,6 +272,8 @@ void GlkEngine::syncSoundSettings() {
 
 	int volume = ConfMan.getBool("sfx_mute") ? 0 : CLIP(ConfMan.getInt("sfx_volume"), 0, 255);
 	_mixer->setVolumeForSoundType(Audio::Mixer::kPlainSoundType, volume);
+
+	SpeechManager::syncSoundSettings();
 }
 
 void GlkEngine::beep() {
diff --git a/engines/glk/speech.cpp b/engines/glk/speech.cpp
index c812a973b1..6707db1bc0 100644
--- a/engines/glk/speech.cpp
+++ b/engines/glk/speech.cpp
@@ -51,6 +51,19 @@ void SpeechManager::releaseSpeechManagerInstance() {
 	}
 }
 
+void SpeechManager::syncSoundSettings() {
+#if defined(USE_TTS)
+	debugC(kDebugSpeech, "SpeechManager::syncSoundSettings");
+	if (_instance && _instance->_ttsMan) {
+		int volume = (ConfMan.getInt("speech_volume") * 100) / 256;
+		if (ConfMan.hasKey("mute") && ConfMan.getBool("mute"))
+			volume = 0;
+		debugC(kDebugSpeech, "Set speech volume to %d", volume);
+		_instance->_ttsMan->setVolume(volume);
+	}
+#endif
+}
+
 SpeechManager::SpeechManager() :
 	_refCount(0)
 #if defined(USE_TTS)
@@ -68,7 +81,7 @@ SpeechManager::SpeechManager() :
 		_ttsMan->setLanguage(ConfMan.get("language"));
 		// Volume
 		int volume = (ConfMan.getInt("speech_volume") * 100) / 256;
-		if (ConfMan.hasKey("mute", "scummvm") && ConfMan.getBool("mute", "scummvm"))
+		if (ConfMan.hasKey("mute") && ConfMan.getBool("mute"))
 			volume = 0;
 		_ttsMan->setVolume(volume);
 		// Voice
diff --git a/engines/glk/speech.h b/engines/glk/speech.h
index 5dfd62809e..400531dfa9 100644
--- a/engines/glk/speech.h
+++ b/engines/glk/speech.h
@@ -45,6 +45,8 @@ public:
 	void purgeSpeech(Speech *);
 	void addSpeech(const uint32 *buf, size_t len, Speech *);
 
+	static void syncSoundSettings();
+
 private:
 	SpeechManager();
 	~SpeechManager();


Commit: 8f3c2e57543b8623d115e6377141cbd012eb5c99
    https://github.com/scummvm/scummvm/commit/8f3c2e57543b8623d115e6377141cbd012eb5c99
Author: Thierry Crozat (criezy at scummvm.org)
Date: 2020-06-29T22:48:57+01:00

Commit Message:
GLK: Simplify SpeechManager

The logic to build the string to speak has been moved back to the
Speech class, and the SpeechManager API has been simplified.

Changed paths:
    engines/glk/speech.cpp
    engines/glk/speech.h


diff --git a/engines/glk/speech.cpp b/engines/glk/speech.cpp
index 6707db1bc0..1342aa1358 100644
--- a/engines/glk/speech.cpp
+++ b/engines/glk/speech.cpp
@@ -67,7 +67,7 @@ void SpeechManager::syncSoundSettings() {
 SpeechManager::SpeechManager() :
 	_refCount(0)
 #if defined(USE_TTS)
-	, _ttsMan(nullptr), _lastSpeechSource(nullptr), _nextSpeechAction(Common::TextToSpeechManager::QUEUE)
+	, _ttsMan(nullptr), _lastSpeechSource(nullptr)
 #endif
 {
 #if defined(USE_TTS)
@@ -108,67 +108,35 @@ SpeechManager::~SpeechManager() {
 #endif
 }
 
-void SpeechManager::flushSpeech(Speech * source) {
+void SpeechManager::speak(const Common::U32String &text, Speech *speechSource) {
 #if defined(USE_TTS)
-	if (source != _lastSpeechSource) {
-		debugC(kDebugSpeech, "SpeechManager::flushSpeech() called with a different source than the last text source");
-		purgeSpeech(source);
-	} else {
-		if (_ttsMan != nullptr && !_speechBuffer.empty()) {
-			// Curently the TextToSpeechManager takes a String, which does not properly support
-			// UTF-32. So convert to UTF-8.
-			Common::String text = _speechBuffer.encode();
-			debugC(kDebugSpeech, "Speaking: \"%s\"", text.c_str());
-			_ttsMan->say(text, _nextSpeechAction, "utf-8");
-			_speechBuffer.clear();
-			_nextSpeechAction = Common::TextToSpeechManager::QUEUE;
+	if (_ttsMan != nullptr) {
+		// If the previous speech is from a different source, interrupt it.
+		// Otherwise queeue the speech.
+		Common::TextToSpeechManager::Action speechAction = Common::TextToSpeechManager::QUEUE;
+		if (speechSource != _lastSpeechSource) {
+			debugC(kDebugSpeech, "Interrupting speech from another source.");
+			speechAction = Common::TextToSpeechManager::INTERRUPT;
+			_lastSpeechSource = speechSource;
 		}
+		// Curently the TextToSpeechManager takes a String, which does not properly support
+		// UTF-32. So convert to UTF-8.
+		Common::String textUtf8 = text.encode();
+		debugC(kDebugSpeech, "Speaking: \"%s\"", textUtf8.c_str());
+		_ttsMan->say(textUtf8, speechAction, "utf-8");
 	}
 #endif
 }
 
-void SpeechManager::purgeSpeech(Speech *source) {
+void SpeechManager::stopSpeech(Speech *speechSource) {
 #if defined(USE_TTS)
-	debugC(kDebugSpeech, "SpeechManager::purgeSpeech()");
-	if (_ttsMan != nullptr) {
-		_speechBuffer.clear();
+	debugC(kDebugSpeech, "SpeechManager::stopSpeech()");
+	// Only interrupt the speech if it is from the given speech source.
+	if (_ttsMan != nullptr && speechSource == _lastSpeechSource)
 		_ttsMan->stop();
-	}
-	_nextSpeechAction = Common::TextToSpeechManager::QUEUE;
-	_lastSpeechSource = source;
-#endif
-}
-
-void SpeechManager::addSpeech(const uint32 *buf, size_t len, Speech *source) {
-#if defined(USE_TTS)
-	if (source != _lastSpeechSource) {
-		debugC(kDebugSpeech, "Flushing SpeechManager buffer for a different speech source");
-		if (!_speechBuffer.empty()) {
-			// Flush the pending speech, but allow interupting it if we flush the text
-			// for the new source before it has finished.
-			flushSpeech(_lastSpeechSource);
-			_nextSpeechAction = Common::TextToSpeechManager::INTERRUPT;
-		}
-		_lastSpeechSource = source;
-	}
-	if (_ttsMan != nullptr) {
-		debugC(1, kDebugSpeech, "SpeechManager add speech");
-		for (int i = 0 ; i < len ; ++i, ++buf) {
-			// Should we automatically flush on new lines without waiting for the call to gli_tts_flush?
-			// Should we also flush on '.', '?', and '!'?
-			//if (*buf == '\n') {
-			//	debugC(1, kDebugSpeech, "Flushing SpeechManager buffer on new line");
-			//	gli_tts_flush();
-			//} else {
-			_speechBuffer += *buf;
-			//}
-		}
-//		debugC(1, kDebugSpeech, "SpeechManager buffer: %s", _speechBuffer.encode().c_str());
-	}
 #endif
 }
 
-
 Speech::Speech() : _speechManager(nullptr) {
 }
 
@@ -187,20 +155,34 @@ void Speech::gli_initialize_tts(void) {
 
 void Speech::gli_tts_flush(void) {
 	debugC(kDebugSpeech, "gli_tts_flush");
-	if (_speechManager)
-		_speechManager->flushSpeech(this);
+	if (_speechManager && !_speechBuffer.empty())
+		_speechManager->speak(_speechBuffer, this);
+	_speechBuffer.clear();
 }
 
 void Speech::gli_tts_purge(void) {
 	debugC(kDebugSpeech, "gli_tts_purge");
-	if (_speechManager)
-		_speechManager->purgeSpeech(this);
+	if (_speechManager) {
+		_speechBuffer.clear();
+		_speechManager->stopSpeech(this);
+	}
 }
 
 void Speech::gli_tts_speak(const uint32 *buf, size_t len) {
 	debugC(1, kDebugSpeech, "gli_tts_speak");
-	if (_speechManager)
-		_speechManager->addSpeech(buf, len, this);
+	if (_speechManager) {
+		for (int i = 0 ; i < len ; ++i, ++buf) {
+			// Should we automatically flush on new lines without waiting for the call to gli_tts_flush?
+			// Should we also flush on '.', '?', and '!'?
+			//if (*buf == '\n') {
+			//	debugC(1, kDebugSpeech, "Flushing SpeechManager buffer on new line");
+			//	gli_tts_flush();
+			//} else {
+			_speechBuffer += *buf;
+			//}
+		}
+		//debugC(1, kDebugSpeech, "SpeechManager buffer: %s", _speechBuffer.encode().c_str());
+	}
 }
 
 void Speech::gli_free_tts(void) {
diff --git a/engines/glk/speech.h b/engines/glk/speech.h
index 400531dfa9..04524231e9 100644
--- a/engines/glk/speech.h
+++ b/engines/glk/speech.h
@@ -25,11 +25,10 @@
 
 #include "common/events.h"
 #include "glk/glk_types.h"
-#include "common/singleton.h"
+#include "common/ustr.h"
 
 #if defined(USE_TTS)
 #include "common/text-to-speech.h"
-#include "common/ustr.h"
 #endif
 
 namespace Glk {
@@ -41,9 +40,8 @@ public:
 	static SpeechManager* getSpeechManagerInstance();
 	void releaseSpeechManagerInstance();
 
-	void flushSpeech(Speech *);
-	void purgeSpeech(Speech *);
-	void addSpeech(const uint32 *buf, size_t len, Speech *);
+	void speak(const Common::U32String &, Speech *);
+	void stopSpeech(Speech *);
 
 	static void syncSoundSettings();
 
@@ -55,9 +53,7 @@ private:
 	int _refCount;
 
 #if defined(USE_TTS)
-	Common::U32String _speechBuffer;
 	Common::TextToSpeechManager *_ttsMan;
-	Common::TextToSpeechManager::Action _nextSpeechAction;
 	Speech *_lastSpeechSource;
 #endif
 };
@@ -66,6 +62,7 @@ private:
 class Speech {
 private:
 	SpeechManager *_speechManager;
+	Common::U32String _speechBuffer;
 
 protected:
 	Speech();


Commit: cbf6b6b6e6d72aaf9a2b2c561f56973198515aa8
    https://github.com/scummvm/scummvm/commit/cbf6b6b6e6d72aaf9a2b2c561f56973198515aa8
Author: Thierry Crozat (criezy at scummvm.org)
Date: 2020-06-29T22:48:57+01:00

Commit Message:
GLK: Add tts flush when redrawing the text buffer window

This adds TTS to a lot of text that was previously not spoken
due to missing flush. And adding a flush when we actually draw the
text to the screen seems to make sense.

Changed paths:
    engines/glk/window_text_buffer.cpp


diff --git a/engines/glk/window_text_buffer.cpp b/engines/glk/window_text_buffer.cpp
index d212d8f783..d341c4e4b2 100644
--- a/engines/glk/window_text_buffer.cpp
+++ b/engines/glk/window_text_buffer.cpp
@@ -822,6 +822,8 @@ void TextBufferWindow::redraw() {
 	int tx, tsc, tsw, lsc, rsc;
 	Screen &screen = *g_vm->_screen;
 
+	gli_tts_flush();
+
 	Window::redraw();
 
 	_lines[0]._len = _numChars;


Commit: 01f3ce620ad193751dad816967a966841cd973c0
    https://github.com/scummvm/scummvm/commit/01f3ce620ad193751dad816967a966841cd973c0
Author: Thierry Crozat (criezy at scummvm.org)
Date: 2020-06-29T22:48:57+01:00

Commit Message:
GLK: Add method that takes c-string text in Speech class

Changed paths:
    engines/glk/speech.cpp
    engines/glk/speech.h


diff --git a/engines/glk/speech.cpp b/engines/glk/speech.cpp
index 1342aa1358..6941c20f72 100644
--- a/engines/glk/speech.cpp
+++ b/engines/glk/speech.cpp
@@ -169,7 +169,7 @@ void Speech::gli_tts_purge(void) {
 }
 
 void Speech::gli_tts_speak(const uint32 *buf, size_t len) {
-	debugC(1, kDebugSpeech, "gli_tts_speak");
+	debugC(1, kDebugSpeech, "gli_tts_speak(const uint32 *, size_t)");
 	if (_speechManager) {
 		for (int i = 0 ; i < len ; ++i, ++buf) {
 			// Should we automatically flush on new lines without waiting for the call to gli_tts_flush?
@@ -185,6 +185,23 @@ void Speech::gli_tts_speak(const uint32 *buf, size_t len) {
 	}
 }
 
+void Speech::gli_tts_speak(const char *buf, size_t len) {
+	debugC(1, kDebugSpeech, "gli_tts_speak(const char *, size_t)");
+	if (_speechManager) {
+		for (int i = 0 ; i < len ; ++i, ++buf) {
+			// Should we automatically flush on new lines without waiting for the call to gli_tts_flush?
+			// Should we also flush on '.', '?', and '!'?
+			//if (*buf == '\n') {
+			//	debugC(1, kDebugSpeech, "Flushing SpeechManager buffer on new line");
+			//	gli_tts_flush();
+			//} else {
+			_speechBuffer += (uint32)*buf;
+			//}
+		}
+		//debugC(1, kDebugSpeech, "SpeechManager buffer: %s", _speechBuffer.encode().c_str());
+	}
+}
+
 void Speech::gli_free_tts(void) {
 	debugC(kDebugSpeech, "gli_free_tts");
 	if (_speechManager) {
diff --git a/engines/glk/speech.h b/engines/glk/speech.h
index 04524231e9..c00766c909 100644
--- a/engines/glk/speech.h
+++ b/engines/glk/speech.h
@@ -75,6 +75,7 @@ protected:
 	void gli_tts_purge(void);
 
 	void gli_tts_speak(const uint32 *buf, size_t len);
+	void gli_tts_speak(const char *buf, size_t len);
 
 	void gli_free_tts(void);
 };


Commit: f35f0f2804517139004f973671f03889cea9127a
    https://github.com/scummvm/scummvm/commit/f35f0f2804517139004f973671f03889cea9127a
Author: Thierry Crozat (criezy at scummvm.org)
Date: 2020-06-29T22:48:57+01:00

Commit Message:
GLK: Implement TTS for the TextGridWindow

Changed paths:
    engines/glk/window_text_grid.cpp
    engines/glk/window_text_grid.h


diff --git a/engines/glk/window_text_grid.cpp b/engines/glk/window_text_grid.cpp
index fb5a3a4bc6..d4839a1d6b 100644
--- a/engines/glk/window_text_grid.cpp
+++ b/engines/glk/window_text_grid.cpp
@@ -41,9 +41,15 @@ TextGridWindow::TextGridWindow(Windows *windows, uint rock) : TextWindow(windows
 	_lineTerminators = nullptr;
 
 	Common::copy(&g_conf->_gStyles[0], &g_conf->_gStyles[style_NUMSTYLES], _styles);
+
+	if (g_conf->_speak)
+		gli_initialize_tts();
 }
 
 TextGridWindow::~TextGridWindow() {
+	if (g_conf->_speak)
+		gli_free_tts();
+
 	if (_inBuf) {
 		if (g_vm->gli_unregister_arr)
 			(*g_vm->gli_unregister_arr)(_inBuf, _inMax, "&+#!Cn", _inArrayRock);
@@ -87,6 +93,12 @@ uint TextGridWindow::getSplit(uint size, bool vertical) const {
 void TextGridWindow::putCharUni(uint32 ch) {
 	TextGridRow *ln;
 
+	// This may not be the best way to do this, but some games use user styles to
+	// display some gliphs from ASCII characters. Those should not be spoken as
+	// they make no sense.
+	if (_attr.style < style_User1)
+		gli_tts_speak(&ch, 1);
+
 	// Canonicalize the cursor position. That is, the cursor may have been
 	// left outside the window area; wrap it if necessary.
 	if (_curX < 0) {
@@ -165,6 +177,11 @@ void TextGridWindow::moveCursor(const Point &pos) {
 	// If the values are negative, they're really huge positive numbers --
 	// remember that they were cast from uint. So set them huge and
 	// let canonicalization take its course.
+	if (_curY >= 0 && _curY < _height && _lines[_curY].dirty) {
+		const uint32 NEWLINE = '\n';
+		gli_tts_speak((const uint32 *)&NEWLINE, 1);
+	}
+
 	_curX = (pos.x < 0) ? 32767 : pos.x;
 	_curY = (pos.y < 0) ? 32767 : pos.y;
 }
@@ -222,6 +239,7 @@ void TextGridWindow::requestLineEvent(char *buf, uint maxlen, uint initlen) {
 	}
 
 	_lineRequest = true;
+	gli_tts_flush();
 
 	if ((int)maxlen > (_width - _curX))
 		maxlen = (_width - _curX);
@@ -277,6 +295,7 @@ void TextGridWindow::requestLineEventUni(uint32 *buf, uint maxlen, uint initlen)
 	}
 
 	_lineRequestUni = true;
+	gli_tts_flush();
 
 	if ((int)maxlen > (_width - _curX))
 		maxlen = (_width - _curX);
@@ -417,6 +436,8 @@ void TextGridWindow::acceptReadChar(uint arg) {
 		key = arg;
 	}
 
+	gli_tts_purge();
+
 	if (key > 0xff && key < (0xffffffff - keycode_MAXVAL + 1)) {
 		if (!(_charRequestUni) || key > 0x10ffff)
 			key = keycode_Unknown;
@@ -442,16 +463,28 @@ void TextGridWindow::acceptLine(uint32 keycode) {
 	inmax = _inMax;
 	inarrayrock = _inArrayRock;
 
+	gli_tts_purge();
+
 	if (!unicode) {
 		for (ix = 0; ix < _inLen; ix++)
 			((char *)inbuf)[ix] = (char)ln->_chars[_inOrgX + ix];
 		if (_echoStream)
 			_echoStream->echoLine((char *)inbuf, _inLen);
+		if (g_conf->_speakInput) {
+			const char NEWLINE = '\n';
+			gli_tts_speak((const char *)inbuf, _inLen);
+			gli_tts_speak((const char *)&NEWLINE, 1);
+		}
 	} else {
 		for (ix = 0; ix < _inLen; ix++)
 			((uint *)inbuf)[ix] = ln->_chars[_inOrgX + ix];
 		if (_echoStream)
 			_echoStream->echoLineUni((const uint32 *)inbuf, _inLen);
+		if (g_conf->_speakInput) {
+			const uint32 NEWLINE = '\n';
+			gli_tts_speak((const uint32 *)inbuf, _inLen);
+			gli_tts_speak((const uint32 *)&NEWLINE, 1);
+		}
 	}
 
 	_curY = _inOrgY + 1;
@@ -595,6 +628,8 @@ void TextGridWindow::redraw() {
 	uint fgcolor, bgcolor;
 	Screen &screen = *g_vm->_screen;
 
+	gli_tts_flush();
+
 	Window::redraw();
 
 	x0 = _bbox.left;
diff --git a/engines/glk/window_text_grid.h b/engines/glk/window_text_grid.h
index 86be763479..60f74b61f0 100644
--- a/engines/glk/window_text_grid.h
+++ b/engines/glk/window_text_grid.h
@@ -25,13 +25,14 @@
 
 #include "glk/windows.h"
 #include "glk/conf.h"
+#include "glk/speech.h"
 
 namespace Glk {
 
 /**
  * Text Grid window
  */
-class TextGridWindow : public TextWindow {
+class TextGridWindow : public TextWindow, Speech {
 	/**
 	 * Structure for a row within the grid window
 	 */


Commit: 64c811b44bfe1aac99993e89f3fd3906d1d325dd
    https://github.com/scummvm/scummvm/commit/64c811b44bfe1aac99993e89f3fd3906d1d325dd
Author: Thierry Crozat (criezy at scummvm.org)
Date: 2020-06-29T22:48:57+01:00

Commit Message:
GLK: Do not interrupt current speech when getting text from a different window

Changed paths:
    engines/glk/speech.cpp


diff --git a/engines/glk/speech.cpp b/engines/glk/speech.cpp
index 6941c20f72..3476b1abf7 100644
--- a/engines/glk/speech.cpp
+++ b/engines/glk/speech.cpp
@@ -115,8 +115,10 @@ void SpeechManager::speak(const Common::U32String &text, Speech *speechSource) {
 		// Otherwise queeue the speech.
 		Common::TextToSpeechManager::Action speechAction = Common::TextToSpeechManager::QUEUE;
 		if (speechSource != _lastSpeechSource) {
-			debugC(kDebugSpeech, "Interrupting speech from another source.");
-			speechAction = Common::TextToSpeechManager::INTERRUPT;
+			debugC(kDebugSpeech, "Changing speack text source.");
+			// Should we interrupt the text from the other source?
+			// Just queueing the text seems to provide a better experience.
+			//speechAction = Common::TextToSpeechManager::INTERRUPT;
 			_lastSpeechSource = speechSource;
 		}
 		// Curently the TextToSpeechManager takes a String, which does not properly support
@@ -131,8 +133,10 @@ void SpeechManager::speak(const Common::U32String &text, Speech *speechSource) {
 void SpeechManager::stopSpeech(Speech *speechSource) {
 #if defined(USE_TTS)
 	debugC(kDebugSpeech, "SpeechManager::stopSpeech()");
-	// Only interrupt the speech if it is from the given speech source.
-	if (_ttsMan != nullptr && speechSource == _lastSpeechSource)
+	// Should we only interrupt the speech if it is from the given speech source.
+	// If we do that we probably want to change speak to interrupt the speech when
+	// called with a different speech source as the current one.
+	if (_ttsMan != nullptr)
 		_ttsMan->stop();
 #endif
 }




More information about the Scummvm-git-logs mailing list