[Scummvm-git-logs] scummvm master -> 0739ef0fbda0d597ff581e17d0ca3879f2a38346
criezy
noreply at scummvm.org
Wed Aug 27 21:21:08 UTC 2025
This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
0739ef0fbd HUGO: Add text-to-speech (TTS)
Commit: 0739ef0fbda0d597ff581e17d0ca3879f2a38346
https://github.com/scummvm/scummvm/commit/0739ef0fbda0d597ff581e17d0ca3879f2a38346
Author: ellm135 (ellm13531 at gmail.com)
Date: 2025-08-27T22:21:04+01:00
Commit Message:
HUGO: Add text-to-speech (TTS)
Changed paths:
engines/hugo/detection.cpp
engines/hugo/detection.h
engines/hugo/display.cpp
engines/hugo/file.cpp
engines/hugo/hugo.cpp
engines/hugo/hugo.h
engines/hugo/intro.cpp
engines/hugo/metaengine.cpp
engines/hugo/mouse.cpp
engines/hugo/parser.cpp
engines/hugo/schedule.cpp
engines/hugo/sound.cpp
engines/hugo/util.cpp
engines/hugo/util.h
diff --git a/engines/hugo/detection.cpp b/engines/hugo/detection.cpp
index e6e79f852d7..e6a2ea27f99 100644
--- a/engines/hugo/detection.cpp
+++ b/engines/hugo/detection.cpp
@@ -59,7 +59,7 @@ static const HugoGameDescription gameDescriptions[] = {
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
- GUIO0()
+ GUIO1(GAMEOPTION_TTS)
},
kGameTypeHugo1
},
@@ -71,7 +71,7 @@ static const HugoGameDescription gameDescriptions[] = {
Common::EN_ANY,
Common::kPlatformWindows,
GF_PACKED,
- GUIO0()
+ GUIO1(GAMEOPTION_TTS)
},
kGameTypeHugo1
},
@@ -83,7 +83,7 @@ static const HugoGameDescription gameDescriptions[] = {
Common::EN_ANY,
Common::kPlatformDOS,
GF_PACKED,
- GUIO0()
+ GUIO1(GAMEOPTION_TTS)
},
kGameTypeHugo2
},
@@ -95,7 +95,7 @@ static const HugoGameDescription gameDescriptions[] = {
Common::EN_ANY,
Common::kPlatformWindows,
GF_PACKED,
- GUIO0()
+ GUIO1(GAMEOPTION_TTS)
},
kGameTypeHugo2
},
@@ -107,7 +107,7 @@ static const HugoGameDescription gameDescriptions[] = {
Common::EN_ANY,
Common::kPlatformDOS,
GF_PACKED,
- GUIO0()
+ GUIO1(GAMEOPTION_TTS)
},
kGameTypeHugo3
},
@@ -119,7 +119,7 @@ static const HugoGameDescription gameDescriptions[] = {
Common::EN_ANY,
Common::kPlatformWindows,
GF_PACKED,
- GUIO0()
+ GUIO1(GAMEOPTION_TTS)
},
kGameTypeHugo3
},
diff --git a/engines/hugo/detection.h b/engines/hugo/detection.h
index 2714f40bd6e..19faef9af1a 100644
--- a/engines/hugo/detection.h
+++ b/engines/hugo/detection.h
@@ -54,6 +54,8 @@ struct HugoGameDescription {
GameType gameType;
};
+#define GAMEOPTION_TTS GUIO_GAMEOPTIONS1
+
} // End of namespace Hugo
#endif // HUGO_DETECTION_H
diff --git a/engines/hugo/display.cpp b/engines/hugo/display.cpp
index ff5a3242930..e0a2943d7e2 100644
--- a/engines/hugo/display.cpp
+++ b/engines/hugo/display.cpp
@@ -73,6 +73,10 @@ static const byte stdMouseCursor[] = {
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1
};
+#ifdef USE_TTS
+static const uint8 kHelpFirstNewlineIndex = 20;
+#endif
+
Screen::Screen(HugoEngine *vm) : _vm(vm) {
_mainPalette = nullptr;
@@ -483,17 +487,25 @@ void Screen::shadowStr(int16 sx, const int16 sy, const char *s, const byte color
* present in the DOS versions
*/
void Screen::userHelp() const {
- Utils::notifyBox(
- "F1 - Press F1 again\n"
- " for instructions\n"
- "F2 - Sound on/off\n"
- "F3 - Recall last line\n"
- "F4 - Save game\n"
- "F5 - Restore game\n"
- "F6 - Inventory\n"
- "F8 - Turbo button\n"
- "\n"
- "ESC - Return to game");
+ Common::String message = "F1 - Press F1 again\n"
+ " for instructions\n"
+ "F2 - Sound on/off\n"
+ "F3 - Recall last line\n"
+ "F4 - Save game\n"
+ "F5 - Restore game\n"
+ "F6 - Inventory\n"
+ "F8 - Turbo button\n"
+ "\n"
+ "ESC - Return to game";
+
+#ifdef USE_TTS
+ // Replace the first newline with a space for smoother voicing
+ message[kHelpFirstNewlineIndex] = ' ';
+ Utils::sayText(message, Common::TextToSpeechManager::INTERRUPT, false);
+ message[kHelpFirstNewlineIndex] = '\n';
+#endif
+
+ Utils::notifyBox(message, false);
}
void Screen::drawStatusText() {
diff --git a/engines/hugo/file.cpp b/engines/hugo/file.cpp
index 86d1cfdd2ce..c1fe3590bc2 100644
--- a/engines/hugo/file.cpp
+++ b/engines/hugo/file.cpp
@@ -450,6 +450,9 @@ bool FileManager::restoreGame(const int16 slot) {
int score = in->readSint16BE();
_vm->setScore(score);
+#ifdef USE_TTS
+ _vm->_previousScore = -1;
+#endif
gameStatus._storyModeFl = (in->readByte() == 1);
_vm->_mouse->setJumpExitFl(in->readByte() == 1);
diff --git a/engines/hugo/hugo.cpp b/engines/hugo/hugo.cpp
index fada0d9c0cd..3f0a30b4bb7 100644
--- a/engines/hugo/hugo.cpp
+++ b/engines/hugo/hugo.cpp
@@ -106,6 +106,13 @@ HugoEngine::HugoEngine(OSystem *syst, const HugoGameDescription *gd) : Engine(sy
_boot._exitLen = 0;
_file = nullptr;
_scheduler = nullptr;
+
+#ifdef USE_TTS
+ _queueAllVoicing = false;
+ _voiceScoreLine = true;
+ _voiceSoundSetting = false;
+ _previousScore = 0;
+#endif
}
HugoEngine::~HugoEngine() {
@@ -283,6 +290,14 @@ Common::Error HugoEngine::run() {
_scheduler->initCypher();
+#ifdef USE_TTS
+ Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
+ if (ttsMan) {
+ ttsMan->enable(ConfMan.getBool("tts_enabled"));
+ ttsMan->setLanguage(ConfMan.get("language"));
+ }
+#endif
+
initStatus(); // Initialize game status
initConfig(); // Initialize user's config
if (!_status._doQuitFl) {
@@ -712,7 +727,7 @@ void HugoEngine::endGame() {
if (_boot._registered != kRegRegistered)
Utils::notifyBox(_text->getTextEngine(kEsAdvertise));
- Utils::notifyBox(Common::String::format("%s\n%s", _episode, getCopyrightString()));
+ Utils::notifyBox(Common::String::format("%s\n%s", _episode, getCopyrightString()), true, false);
_status._viewState = kViewExit;
}
@@ -738,4 +753,26 @@ Common::String HugoEngine::getSaveStateName(int slot) const {
return _targetName + Common::String::format("-%02d.SAV", slot);
}
+#ifdef USE_TTS
+
+void HugoEngine::sayText(const Common::String &text, Common::TextToSpeechManager::Action action) {
+ // _previousSaid is used to prevent the TTS from looping when sayText is called inside a loop,
+ // for example when the user hovers over an object. Without it when the text ends it would speak
+ // the same text again.
+ // _previousSaid is cleared when appropriate to allow for repeat requests
+ if (_previousSaid != text) {
+ Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
+ if (ttsMan && ttsMan->isSpeaking() && _queueAllVoicing) {
+ Utils::sayText(text, Common::TextToSpeechManager::QUEUE, false);
+ } else {
+ Utils::sayText(text, action, false);
+ _queueAllVoicing = false;
+ }
+
+ _previousSaid = text;
+ }
+}
+
+#endif
+
} // End of namespace Hugo
diff --git a/engines/hugo/hugo.h b/engines/hugo/hugo.h
index e43497e8c7b..c17dea64782 100644
--- a/engines/hugo/hugo.h
+++ b/engines/hugo/hugo.h
@@ -22,6 +22,8 @@
#ifndef HUGO_HUGO_H
#define HUGO_HUGO_H
+#include "common/text-to-speech.h"
+
#include "engines/engine.h"
// This include is here temporarily while the engine is being refactored.
@@ -245,6 +247,15 @@ public:
Command _statusLine;
Command _scoreLine;
+#ifdef USE_TTS
+ bool _voiceScoreLine;
+ bool _voiceSoundSetting;
+ bool _queueAllVoicing;
+ int _previousScore;
+
+ Common::String _previousSaid;
+#endif
+
const HugoGameDescription *_gameDescription;
uint32 getFeatures() const;
const char *getGameId() const;
@@ -289,6 +300,10 @@ public:
Common::String getSaveStateName(int slot) const override;
uint16 **loadLongArray(Common::SeekableReadStream &in);
+#ifdef USE_TTS
+ void sayText(const Common::String &text, Common::TextToSpeechManager::Action action = Common::TextToSpeechManager::INTERRUPT);
+#endif
+
FileManager *_file;
Scheduler *_scheduler;
Screen *_screen;
diff --git a/engines/hugo/intro.cpp b/engines/hugo/intro.cpp
index f415a808efe..943f76018c4 100644
--- a/engines/hugo/intro.cpp
+++ b/engines/hugo/intro.cpp
@@ -104,6 +104,14 @@ bool intro_v1d::introPlay() {
if (_vm->getGameStatus()._skipIntroFl)
return true;
+#ifdef USE_TTS
+ Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
+ if (ttsMan && ttsMan->isSpeaking()) {
+ return false;
+ }
+
+ Common::String ttsMessage;
+#endif
if (_introTicks < introSize) {
switch (_introState++) {
case 0:
@@ -132,9 +140,21 @@ bool intro_v1d::introPlay() {
_font.drawString(&_surf, buffer, 0, 163, 320, _TLIGHTMAGENTA, Graphics::kTextAlignCenter);
_font.drawString(&_surf, _vm->getCopyrightString(), 0, 176, 320, _TLIGHTMAGENTA, Graphics::kTextAlignCenter);
+#ifdef USE_TTS
+ ttsMessage = "Hugo's House of Horrors\n\n";
+ ttsMessage += buffer;
+ ttsMessage += '\n';
+ ttsMessage += _vm->getCopyrightString();
+#endif
+
if ((*_vm->_boot._distrib != '\0') && (scumm_stricmp(_vm->_boot._distrib, "David P. Gray"))) {
Common::sprintf_s(buffer, "Distributed by %s.", _vm->_boot._distrib);
_font.drawString(&_surf, buffer, 0, 75, 320, _TMAGENTA, Graphics::kTextAlignCenter);
+#ifdef USE_TTS
+ // Insert at second newline after "Hugo's House of Horrors" so that a newline remains between that line
+ // and this one
+ ttsMessage.insertString(buffer, ttsMessage.find('\n') + 1);
+#endif
}
// SCRIPT, size 24-16
@@ -164,6 +184,9 @@ bool intro_v1d::introPlay() {
Common::strcpy_s(buffer, "S t a r r i n g :");
_font.drawString(&_surf, buffer, 0, 95, 320, _TMAGENTA, Graphics::kTextAlignCenter);
+#ifdef USE_TTS
+ ttsMessage = "Starring:";
+#endif
break;
case 3:
// TROMAN, size 20-9
@@ -172,6 +195,9 @@ bool intro_v1d::introPlay() {
Common::strcpy_s(buffer, "Hugo !");
_font.drawString(&_surf, buffer, 0, 115, 320, _TLIGHTMAGENTA, Graphics::kTextAlignCenter);
+#ifdef USE_TTS
+ ttsMessage = buffer;
+#endif
break;
case 4:
_vm->_screen->drawRectangle(true, 82, 92, 237, 138, _TBLACK);
@@ -182,11 +208,17 @@ bool intro_v1d::introPlay() {
Common::strcpy_s(buffer, "P r o d u c e d b y :");
_font.drawString(&_surf, buffer, 0, 95, 320, _TMAGENTA, Graphics::kTextAlignCenter);
+#ifdef USE_TTS
+ ttsMessage = "Produced by:";
+#endif
break;
case 5:
// TROMAN size 16-9
Common::strcpy_s(buffer, "David P Gray !");
_font.drawString(&_surf, buffer, 0, 115, 320, _TLIGHTMAGENTA, Graphics::kTextAlignCenter);
+#ifdef USE_TTS
+ ttsMessage = buffer;
+#endif
break;
case 6:
_vm->_screen->drawRectangle(true, 82, 92, 237, 138, _TBLACK);
@@ -194,11 +226,17 @@ bool intro_v1d::introPlay() {
// TROMAN, size 16-9
Common::strcpy_s(buffer, "D i r e c t e d b y :");
_font.drawString(&_surf, buffer, 0, 95, 320, _TMAGENTA, Graphics::kTextAlignCenter);
+#ifdef USE_TTS
+ ttsMessage = "Directed by:";
+#endif
break;
case 7:
// TROMAN, size 16-9
Common::strcpy_s(buffer, "David P Gray !");
_font.drawString(&_surf, buffer, 0, 115, 320, _TLIGHTMAGENTA, Graphics::kTextAlignCenter);
+#ifdef USE_TTS
+ ttsMessage = buffer;
+#endif
break;
case 8:
_vm->_screen->drawRectangle(true, 82, 92, 237, 138, _TBLACK);
@@ -206,11 +244,17 @@ bool intro_v1d::introPlay() {
// TROMAN, size 16-9
Common::strcpy_s(buffer, "M u s i c b y :");
_font.drawString(&_surf, buffer, 0, 95, 320, _TMAGENTA, Graphics::kTextAlignCenter);
+#ifdef USE_TTS
+ ttsMessage = "Music by:";
+#endif
break;
case 9:
// TROMAN, size 16-9
Common::strcpy_s(buffer, "David P Gray !");
_font.drawString(&_surf, buffer, 0, 115, 320, _TLIGHTMAGENTA, Graphics::kTextAlignCenter);
+#ifdef USE_TTS
+ ttsMessage = buffer;
+#endif
break;
case 10:
_vm->_screen->drawRectangle(true, 82, 92, 237, 138, _TBLACK);
@@ -221,11 +265,18 @@ bool intro_v1d::introPlay() {
Common::strcpy_s(buffer, "E n j o y !");
_font.drawString(&_surf, buffer, 0, 100, 320, _TLIGHTMAGENTA, Graphics::kTextAlignCenter);
+#ifdef USE_TTS
+ ttsMessage = "Enjoy!";
+#endif
break;
default:
break;
}
+#ifdef USE_TTS
+ _vm->sayText(ttsMessage);
+#endif
+
_vm->_screen->displayBackground();
g_system->updateScreen();
g_system->delayMillis(1000);
@@ -261,15 +312,34 @@ void intro_v2d::introInit() {
_font.drawString(&_surf, buffer, 0, 186, 320, _TLIGHTRED, Graphics::kTextAlignCenter);
+#ifdef USE_TTS
+ Common::String ttsMessage = buffer;
+#endif
+
if ((*_vm->_boot._distrib != '\0') && (scumm_stricmp(_vm->_boot._distrib, "David P. Gray"))) {
// TROMAN, size 10-5
Common::sprintf_s(buffer, "Distributed by %s.", _vm->_boot._distrib);
_font.drawString(&_surf, buffer, 0, 1, 320, _TLIGHTRED, Graphics::kTextAlignCenter);
+
+#ifdef USE_TTS
+ ttsMessage = buffer + ttsMessage;
+#endif
}
+#ifdef USE_TTS
+ _vm->sayText(ttsMessage);
+#endif
+
_vm->_screen->displayBackground();
g_system->updateScreen();
g_system->delayMillis(5000);
+
+#ifdef USE_TTS
+ Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
+ while (ttsMan && ttsMan->isSpeaking()) {
+ g_system->delayMillis(10);
+ }
+#endif
}
bool intro_v2d::introPlay() {
@@ -302,15 +372,34 @@ void intro_v3d::introInit() {
_font.drawString(&_surf, buffer, 0, 190, 320, _TBROWN, Graphics::kTextAlignCenter);
+#ifdef USE_TTS
+ Common::String ttsMessage = buffer;
+#endif
+
if ((*_vm->_boot._distrib != '\0') && (scumm_stricmp(_vm->_boot._distrib, "David P. Gray"))) {
Common::sprintf_s(buffer, "Distributed by %s.", _vm->_boot._distrib);
_font.drawString(&_surf, buffer, 0, 0, 320, _TBROWN, Graphics::kTextAlignCenter);
+
+#ifdef USE_TTS
+ ttsMessage = buffer + ttsMessage;
+#endif
}
+#ifdef USE_TTS
+ _vm->sayText(ttsMessage);
+#endif
+
_vm->_screen->displayBackground();
g_system->updateScreen();
g_system->delayMillis(5000);
+#ifdef USE_TTS
+ Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
+ while (ttsMan && ttsMan->isSpeaking()) {
+ g_system->delayMillis(10);
+ }
+#endif
+
_vm->_file->readBackground(22); // display screen MAP_3d
_vm->_screen->displayBackground();
_introTicks = 0;
diff --git a/engines/hugo/metaengine.cpp b/engines/hugo/metaengine.cpp
index e1e572a6c09..bb842185fd0 100644
--- a/engines/hugo/metaengine.cpp
+++ b/engines/hugo/metaengine.cpp
@@ -36,6 +36,26 @@
namespace Hugo {
+#ifdef USE_TTS
+
+static const ADExtraGuiOptionsMap optionsList[] = {
+ {
+ GAMEOPTION_TTS,
+ {
+ _s("Enable Text to Speech"),
+ _s("Use TTS to read text in the game (if TTS is available)"),
+ "tts_enabled",
+ false,
+ 0,
+ 0
+ }
+ },
+
+ AD_EXTRA_GUI_OPTIONS_TERMINATOR
+};
+
+#endif
+
uint32 HugoEngine::getFeatures() const {
return _gameDescription->desc.flags;
}
@@ -50,6 +70,12 @@ public:
return "hugo";
}
+#ifdef USE_TTS
+ const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override {
+ return optionsList;
+ }
+#endif
+
Common::Error createInstance(OSystem *syst, Engine **engine, const HugoGameDescription *gd) const override;
bool hasFeature(MetaEngineFeature f) const override;
diff --git a/engines/hugo/mouse.cpp b/engines/hugo/mouse.cpp
index 48cba2e240c..8a0717f8d67 100644
--- a/engines/hugo/mouse.cpp
+++ b/engines/hugo/mouse.cpp
@@ -142,6 +142,10 @@ void MouseHandler::cursorText(const char *buffer, const int16 cx, const int16 cy
// Display the string and add rect to display list
_vm->_screen->shadowStr(sx, sy, buffer, _TBRIGHTWHITE);
_vm->_screen->displayList(kDisplayAdd, sx, sy, sdx, sdy);
+
+#ifdef USE_TTS
+ _vm->sayText(buffer);
+#endif
}
/**
@@ -334,6 +338,11 @@ void MouseHandler::mouseHandler() {
const char *name = _vm->_text->getNoun(_vm->_object->_objects[(objId == kHeroIndex) ? _vm->_heroImage : objId]._nounIndex, kCursorNameIndex);
if (name[0] != kCursorNochar)
cursorText(name, cx, cy, U_FONT8, _TBRIGHTWHITE);
+#ifdef USE_TTS
+ else {
+ _vm->_previousSaid.clear();
+ }
+#endif
// Process right click over object in view or iconbar
if (_rightButtonFl)
@@ -347,6 +356,11 @@ void MouseHandler::mouseHandler() {
objId = kExitHotspot;
cursorText(_vm->_text->getTextMouse(kMsExit), cx, cy, U_FONT8, _TBRIGHTWHITE);
}
+#ifdef USE_TTS
+ else {
+ _vm->_previousSaid.clear();
+ }
+#endif
}
}
// Left click over icon, object or to move somewhere
diff --git a/engines/hugo/parser.cpp b/engines/hugo/parser.cpp
index 632d5317e1b..466f0b9feb4 100644
--- a/engines/hugo/parser.cpp
+++ b/engines/hugo/parser.cpp
@@ -40,6 +40,7 @@
#include "hugo/object.h"
#include "hugo/text.h"
#include "hugo/inventory.h"
+#include "hugo/mouse.h"
namespace Hugo {
@@ -208,6 +209,12 @@ void Parser::freeParser() {
void Parser::switchTurbo() {
_vm->_config._turboFl = !_vm->_config._turboFl;
+
+#ifdef USE_TTS
+ if (_vm->_config._turboFl) {
+ _vm->sayText("T");
+ }
+#endif
}
/**
@@ -235,6 +242,10 @@ void Parser::charHandler() {
// Remove inventory bar if active
if (_vm->_inventory->getInventoryState() == kInventoryActive)
_vm->_inventory->setInventoryState(kInventoryUp);
+#ifdef USE_TTS
+ _vm->sayText(_cmdLine);
+ _vm->_previousSaid.clear();
+#endif
// Call Line handler and reset line
command(_cmdLine);
_cmdLine[_cmdLineIndex = 0] = '\0';
@@ -267,6 +278,30 @@ void Parser::charHandler() {
Common::sprintf_s(_vm->_statusLine, ">%s%c", _cmdLine, _cmdLineCursor);
Common::sprintf_s(_vm->_scoreLine, "F1-Help %s Score: %d of %d Sound %s", (_vm->_config._turboFl) ? "T" : " ", _vm->getScore(), _vm->getMaxScore(), (_vm->_config._soundFl) ? "On" : "Off");
+#ifdef USE_TTS
+ if (_vm->_previousScore != _vm->getScore()) {
+ _vm->sayText(Common::String::format("Score: %d of %d", _vm->getScore(), _vm->getMaxScore()));
+ _vm->_previousScore = _vm->getScore();
+ }
+
+ if (_vm->_voiceScoreLine) {
+ _vm->sayText(Common::String::format("F1: Help\n%s\nScore: %d of %d\nSound %s", (_vm->_config._turboFl) ? "T" : " ", _vm->getScore(), _vm->getMaxScore(), (_vm->_config._soundFl) ? "On" : "Off"));
+ _vm->_voiceScoreLine = false;
+ }
+
+ if (_vm->_voiceSoundSetting) {
+ _vm->sayText(Common::String::format("Sound %s", (_vm->_config._soundFl) ? "On" : "Off"));
+
+ // If the mouse is in the top menu range, the top menu will close and reopen after the sound setting is changed,
+ // causing the new sound setting voicing to be interrupted. Therefore, keep trying to voice the new sound setting
+ // as long as the top menu can be opened
+ int mouseY = _vm->_mouse->getMouseY();
+ if (mouseY <= 0 || mouseY >= 5) {
+ _vm->_voiceSoundSetting = false;
+ }
+ }
+#endif
+
// See if "look" button pressed
if (gameStatus._lookFl) {
command("look around");
@@ -452,18 +487,32 @@ void Parser::showDosInventory() const {
buffer += Common::String(_vm->_text->getTextParser(kTBIntro)) + "\n";
index = 0;
+#ifdef USE_TTS
+ Common::String ttsMessage = buffer;
+#endif
for (int i = 0; i < _vm->_object->_numObj; i++) { // Assign strings
if (_vm->_object->isCarried(i)) {
- if (index++ & 1)
+ if (index++ & 1) {
buffer += Common::String(_vm->_text->getNoun(_vm->_object->_objects[i]._nounIndex, 2)) + "\n";
- else
+#ifdef USE_TTS
+ ttsMessage += Common::String(_vm->_text->getNoun(_vm->_object->_objects[i]._nounIndex, 2)) + "\n";
+#endif
+ } else {
buffer += Common::String(_vm->_text->getNoun(_vm->_object->_objects[i]._nounIndex, 2)) + Common::String(blanks, len1 - strlen(_vm->_text->getNoun(_vm->_object->_objects[i]._nounIndex, 2)));
+#ifdef USE_TTS
+ ttsMessage += Common::String(_vm->_text->getNoun(_vm->_object->_objects[i]._nounIndex, 2)) + "\n" + Common::String(blanks, len1 - strlen(_vm->_text->getNoun(_vm->_object->_objects[i]._nounIndex, 2)));
+#endif
+ }
}
}
if (index & 1)
buffer += "\n";
buffer += Common::String(_vm->_text->getTextParser(kTBOutro));
- Utils::notifyBox(buffer.c_str());
+#ifdef USE_TTS
+ ttsMessage += Common::String(_vm->_text->getTextParser(kTBOutro));
+ _vm->sayText(ttsMessage, Common::TextToSpeechManager::INTERRUPT);
+#endif
+ Utils::notifyBox(buffer.c_str(), false);
}
} // End of namespace Hugo
diff --git a/engines/hugo/schedule.cpp b/engines/hugo/schedule.cpp
index 0b5c5ec4fac..e065ffd6c81 100644
--- a/engines/hugo/schedule.cpp
+++ b/engines/hugo/schedule.cpp
@@ -1550,6 +1550,10 @@ void Scheduler_v1d::promptAction(Act *action) {
response = Utils::promptBox(_vm->_file->fetchString(action->_a3._promptIndex));
+#ifdef USE_TTS
+ _vm->_queueAllVoicing = true;
+#endif
+
response.toLowercase();
char resp[256];
@@ -1594,6 +1598,10 @@ void Scheduler_v2d::promptAction(Act *action) {
response = Utils::promptBox(_vm->_file->fetchString(action->_a3._promptIndex));
response.toLowercase();
+#ifdef USE_TTS
+ _vm->_queueAllVoicing = true;
+#endif
+
debug(1, "doAction(act3), expecting answer %s", _vm->_file->fetchString(action->_a3._responsePtr[0]));
bool found = false;
diff --git a/engines/hugo/sound.cpp b/engines/hugo/sound.cpp
index f8a224a373f..5e4b917bdb5 100644
--- a/engines/hugo/sound.cpp
+++ b/engines/hugo/sound.cpp
@@ -171,6 +171,10 @@ void SoundHandler::toggleMusic() {
*/
void SoundHandler::toggleSound() {
_vm->_config._soundFl = !_vm->_config._soundFl;
+
+#ifdef USE_TTS
+ _vm->_voiceSoundSetting = true;
+#endif
}
void SoundHandler::playMIDI(SoundPtr seqPtr, uint16 size) {
diff --git a/engines/hugo/util.cpp b/engines/hugo/util.cpp
index eb02c694819..748d1035ec5 100644
--- a/engines/hugo/util.cpp
+++ b/engines/hugo/util.cpp
@@ -85,7 +85,13 @@ void reverseByte(byte *data) {
*data = result;
}
-void notifyBox(const Common::String &msg) {
+void notifyBox(const Common::String &msg, bool ttsVoiceText, bool ttsReplaceNewlines) {
+#ifdef USE_TTS
+ if (ttsVoiceText) {
+ sayText(msg, Common::TextToSpeechManager::QUEUE, ttsReplaceNewlines);
+ }
+#endif
+
notifyBox(Common::U32String(msg));
}
@@ -95,20 +101,38 @@ void notifyBox(const Common::U32String &msg) {
GUI::MessageDialog dialog(msg);
dialog.runModal();
+
+#ifdef USE_TTS
+ stopTextToSpeech();
+#endif
}
Common::String promptBox(const Common::String &msg) {
if (msg.empty())
return Common::String();
+#ifdef USE_TTS
+ sayText(msg, Common::TextToSpeechManager::QUEUE);
+#endif
+
EntryDialog dialog(msg, "OK", "");
dialog.runModal();
- return dialog.getEditString();
+ Common::U32String input = dialog.getEditString();
+
+#ifdef USE_TTS
+ sayText(input);
+#endif
+
+ return input;
}
bool yesNoBox(const Common::String &msg) {
+#ifdef USE_TTS
+ sayText(msg, Common::TextToSpeechManager::QUEUE);
+#endif
+
return yesNoBox(Common::U32String(msg));
}
@@ -117,7 +141,14 @@ bool yesNoBox(const Common::U32String &msg) {
return 0;
GUI::MessageDialog dialog(msg, Common::U32String("YES"), Common::U32String("NO"));
- return (dialog.runModal() == GUI::kMessageOK);
+
+ int result = dialog.runModal();
+
+#ifdef USE_TTS
+ stopTextToSpeech();
+#endif
+
+ return (result == GUI::kMessageOK);
}
char *hugo_strlwr(char *buffer) {
@@ -132,6 +163,39 @@ char *hugo_strlwr(char *buffer) {
return result;
}
+#ifdef USE_TTS
+
+void sayText(const Common::String &text, Common::TextToSpeechManager::Action action, bool replaceNewlines) {
+ Common::String ttsMessage(text);
+
+ if (replaceNewlines) {
+ ttsMessage.replace('\n', ' ');
+ ttsMessage.replace('\r', ' ');
+ }
+
+ // Some text is enclosed in < and >, which won't be voiced unless they're replaced
+ Common::replace(ttsMessage, "<", ", ");
+ Common::replace(ttsMessage, ">", ", ");
+
+ sayText(Common::U32String(ttsMessage, Common::CodePage::kWindows1252), action);
+}
+
+void sayText(const Common::U32String &text, Common::TextToSpeechManager::Action action) {
+ Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
+ if (ttsMan && ConfMan.getBool("tts_enabled")) {
+ ttsMan->say(text, action);
+ }
+}
+
+void stopTextToSpeech() {
+ Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
+ if (ttsMan && ConfMan.getBool("tts_enabled") && ttsMan->isSpeaking()) {
+ ttsMan->stop();
+ }
+}
+
+#endif
+
} // End of namespace Utils
} // End of namespace Hugo
diff --git a/engines/hugo/util.h b/engines/hugo/util.h
index 0305b3cc6e2..ed270033db9 100644
--- a/engines/hugo/util.h
+++ b/engines/hugo/util.h
@@ -46,7 +46,7 @@ void reverseByte(byte *data);
* Show a dialog notifying the user about something, with
* only a simple "OK" button to dismiss it.
*/
-void notifyBox(const Common::String &msg); // Redirect to call notifyBox with u32strings
+void notifyBox(const Common::String &msg, bool ttsVoiceText = true, bool ttsReplaceNewlines = true); // Redirect to call notifyBox with u32strings
void notifyBox(const Common::U32String &msg);
/**
@@ -67,6 +67,20 @@ bool yesNoBox(const Common::U32String &msg);
*/
char *hugo_strlwr(char *buffer);
+#ifdef USE_TTS
+/**
+ * Voice text with the text-to-speech system.
+ */
+void sayText(const Common::String &text, Common::TextToSpeechManager::Action action = Common::TextToSpeechManager::INTERRUPT,
+ bool replaceNewlines = true); // Clean up text, then redirect to call sayText with u32strings
+void sayText(const Common::U32String &text, Common::TextToSpeechManager::Action action = Common::TextToSpeechManager::INTERRUPT);
+
+/**
+ * Stop TTS voicing.
+ */
+void stopTextToSpeech();
+#endif
+
} // End of namespace Utils
} // End of namespace Hugo
More information about the Scummvm-git-logs
mailing list