Note
It’s just an overview.
Find the Text
In Griffon’s case, the text is in the code
So just search for “Ever since I was a child”, and there it is, in cutscenes.cpp
const char *story[48] = {
"The Griffon Legend",
"http://syn9.thehideoutgames.com/",
"",
"Programming/Graphics: Daniel Kennedy",
"Music/Sound effects: David Turner",
"",
"Porting to GCW-Zero: Dmitry Smagin",
"",
"",
"Story",
"Ever since I was a child",
"I remember being told the",
"Legend of the Griffon Knights,",
Pass to Text To Speech Manager
Then, we would like to pass the text to text to speech manager
To call for TTS manager, first, include this in the header
#include "common/text-to-speech.h"
Then initialize TTS manager like this
Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
Before passing the text to it, we check to see if it is null pointer, to avoid a crash
if (ttsMan != nullptr)
ttsMan->say("The Griffon Legend");
We also would like to check if TTS has been enabled already
(This works given that there are proper adjustments to detection.cpp about “tts_enabed”)
#include "common/config-manager.h"
if (ttsMan != nullptr && ConfMan.getBool("tts_enabled"))
ttsMan->say("The Griffon Legend");
Obviously we would like the whole story to have TTS, so we loop through
for (int i = 0; i < ARRAYSIZE(story); i++) {
// Do the TTS thing
}
Staying in pace
We would like to have the text shown on the screen be in pace with the TTS speaker.
Therefore, we would like to use the same for loop that is used for showing the text on the screen
See this in the same file, under void GriffonEngine::intro()
, there is a do
{
for (int i = 0; i < ARRAYSIZE(story); i++) { // go through the story array
int yy = y + i * 10; // calculate position
if (yy > -8 && yy < 240) {
int x = 160 - strlen(story[i]) * 4; // when time comes
drawString(_videoBuffer, story[i], x, yy, 4); // draw text to the screen
}
if (yy < 10 && i == ARRAYSIZE(story) - 1) { // last paragraph has left the screen
return; // proceed to game
}
Because there are multiple lines appearing on the screen at once, we need a way to keep track of what has been narrated, and what hasn’t yet
int nextparagraph = 0; //this is new
for (int i = 0; i < ARRAYSIZE(story); i++) {
int yy = y + i * 10;
// if (i == nextparagraph)
// do Text To Speech
// update nextparagraph to reflect progress
if (yy > -8 && yy < 240) {
int x = 160 - strlen(story[i]) * 4;
drawString(_videoBuffer, story[i], x, yy, 4);
}
if (yy < 10 && i == ARRAYSIZE(story) - 1) {
return;
}
There are also two more issues to be addressed:
- It’s better to glue sentences that belong to the same paragraph together. The narrating sounds more natural
- Queue new paragraphs behind the current paragraph that is being spoken
We ended up doing this
int textToSpeech(int nextparagraph, const char *storyVariable[], int arraysize) {
Common::TextToSpeechManager *ttsMan = g_system->getTextToSpeechManager();
if (ttsMan != nullptr && ConfMan.getBool("tts_enabled") && storyVariable[nextparagraph][0] != 0) {
Common::String paragraph;
while (nextparagraph < arraysize && storyVariable[nextparagraph][0] != ' ') {
if (!paragraph.empty())
paragraph += " ";
paragraph += storyVariable[nextparagraph++];
}
while (nextparagraph < arraysize && storyVariable[nextparagraph][0] == ' ') {
nextparagraph += 1;
}
ttsMan->say(paragraph, Common::TextToSpeechManager::QUEUE_NO_REPEAT);
}
return nextparagraph;
}
And calling it with this
if (i == nextparagraph)
nextparagraph = textToSpeech(nextparagraph, story, ARRAYSIZE(story));
Enter game after TTS is finished
Check if ttsMan is speaking (unless it’s nullptr) before return
if (ttsMan == nullptr || ttsMan->isSpeaking() == false)
return;
Stop TTS if intro is interrupted
If we press escape, the intro ends and so should the narrating
else if (_event.customType == kGriffonMenu) {
if (ttsMan != nullptr)
ttsMan->stop();
return;
And That’s about it!
Full changes can be found here.