diff --git a/libultraship/libultraship/Window.cpp b/libultraship/libultraship/Window.cpp index 00868a91d..d929b5ea3 100644 --- a/libultraship/libultraship/Window.cpp +++ b/libultraship/libultraship/Window.cpp @@ -279,21 +279,22 @@ namespace Ship { WmApi->set_keyboard_callbacks(Window::KeyDown, Window::KeyUp, Window::AllKeysUp); } - void task1(const char textToRead[]) + void task1(const std::string & textToRead) { const int w = 512; int* wp = const_cast (&w); - *wp = strlen(textToRead); + *wp = strlen(textToRead.c_str()); wchar_t wtext[w]; - mbstowcs(wtext, textToRead, strlen(textToRead) + 1); + mbstowcs(wtext, textToRead.c_str(), strlen(textToRead.c_str()) + 1); pVoice->Speak(wtext, SPF_IS_XML | SPF_ASYNC | SPF_PURGEBEFORESPEAK, NULL); } void Window::ReadText(const char textToRead[]) { - std::thread t1(task1, textToRead); + std::string textCopy(textToRead); + std::thread t1(task1, textCopy); t1.detach(); } diff --git a/soh/src/code/z_message_PAL.c b/soh/src/code/z_message_PAL.c index f932d3406..2439a8edd 100644 --- a/soh/src/code/z_message_PAL.c +++ b/soh/src/code/z_message_PAL.c @@ -118,6 +118,11 @@ s16 sOcarinaNoteCEnvR; s16 sOcarinaNoteCEnvB; s16 sOcarinaNoteCEnvG; +static u8 sTtsHasNewMessage; +static u8 sTtsHasMessage; +static s8 sTtsCurrentChoice; +static u8 sTtsMessageBuf[256]; + void Message_ResetOcarinaNoteState(void) { R_OCARINA_NOTES_YPOS(0) = 189; R_OCARINA_NOTES_YPOS(1) = 184; @@ -3004,6 +3009,108 @@ void Message_Draw(GlobalContext* globalCtx) { CLOSE_DISPS(globalCtx->state.gfxCtx, "../z_message_PAL.c", 3582); } +void Message_TTS_Decode(u8* srcBuf, u8* dstBuf, u32 srcOffset, u32 size) { + u32 dstIdx = 0; + + for (u32 i = 0; i < size; i++) { + u8 currChar = srcBuf[i + srcOffset]; + + if (currChar < ' ') { + switch (currChar) { + case MESSAGE_NEWLINE: + dstBuf[dstIdx++] = ' '; + break; + case MESSAGE_COLOR: + case MESSAGE_SHIFT: + case MESSAGE_TEXT_SPEED: + case MESSAGE_BOX_BREAK_DELAYED: + case MESSAGE_FADE: + case MESSAGE_ITEM_ICON: + i++; + break; + case MESSAGE_FADE2: + case MESSAGE_SFX: + case MESSAGE_TEXTID: + i += 2; + break; + default: + break; + } + } else { + dstBuf[dstIdx++] = currChar; + } + } + + dstBuf[dstIdx] = 0; +} + +void Message_TTS_Update(GlobalContext* globalCtx) { + MessageContext* msgCtx = &globalCtx->msgCtx; + + if (msgCtx->msgMode == MSGMODE_TEXT_NEXT_MSG || msgCtx->msgMode == MSGMODE_DISPLAY_SONG_PLAYED_TEXT_BEGIN || + (msgCtx->msgMode == MSGMODE_TEXT_CONTINUING && msgCtx->stateTimer == 1)) { + sTtsHasNewMessage = 1; + } else if (msgCtx->msgMode == MSGMODE_TEXT_DISPLAYING || msgCtx->msgMode == MSGMODE_OCARINA_STARTING || + msgCtx->msgMode == MSGMODE_OCARINA_PLAYING || msgCtx->msgMode == MSGMODE_TEXT_AWAIT_NEXT || + msgCtx->msgMode == MSGMODE_TEXT_DONE || msgCtx->msgMode == MSGMODE_DISPLAY_SONG_PLAYED_TEXT || + msgCtx->msgMode == MSGMODE_TEXT_DELAYED_BREAK) { + if (sTtsHasNewMessage == 1) { + sTtsHasNewMessage = 0; + sTtsHasMessage = 1; + sTtsCurrentChoice = 0; + + u32 size = msgCtx->decodedTextLen; + Message_TTS_Decode(msgCtx->msgBufDecoded, sTtsMessageBuf, 0, size); + OTRTextToSpeechCallback(sTtsMessageBuf); + } else if (msgCtx->msgMode == MSGMODE_TEXT_DONE && msgCtx->choiceNum > 0 && + msgCtx->choiceIndex != sTtsCurrentChoice) { + sTtsCurrentChoice = msgCtx->choiceIndex; + u32 startOffset = 0; + u32 endOffset = 0; + while (startOffset < msgCtx->decodedTextLen) { + if (msgCtx->msgBufDecoded[startOffset] == MESSAGE_TWO_CHOICE || + msgCtx->msgBufDecoded[startOffset] == MESSAGE_THREE_CHOICE) { + startOffset++; + break; + } + startOffset++; + } + if (startOffset < msgCtx->decodedTextLen) { + u8 i = msgCtx->choiceIndex; + while (i-- > 0) { + while (startOffset < msgCtx->decodedTextLen) { + if (msgCtx->msgBufDecoded[startOffset] == MESSAGE_NEWLINE) { + startOffset++; + break; + } + startOffset++; + } + } + endOffset = startOffset; + while (endOffset < msgCtx->decodedTextLen) { + if (msgCtx->msgBufDecoded[endOffset] == MESSAGE_NEWLINE) { + break; + } + endOffset++; + } + + if (startOffset < msgCtx->decodedTextLen && startOffset != endOffset) { + u32 size = endOffset - startOffset; + Message_TTS_Decode(msgCtx->msgBufDecoded, sTtsMessageBuf, startOffset, size); + OTRTextToSpeechCallback(sTtsMessageBuf); + } + } + } + } else if (sTtsHasMessage == 1) { + sTtsHasMessage = 0; + sTtsHasNewMessage = 0; + if (msgCtx->decodedTextLen < 3 || (msgCtx->msgBufDecoded[msgCtx->decodedTextLen - 2] != MESSAGE_FADE && + msgCtx->msgBufDecoded[msgCtx->decodedTextLen - 3] != MESSAGE_FADE2)) { + OTRTextToSpeechCallback(""); // cancel current speech (except for faded out messages) + } + } +} + void Message_Update(GlobalContext* globalCtx) { static s16 sTextboxXPositions[] = { 34, 34, 34, 34, 34, 34, @@ -3067,6 +3174,10 @@ void Message_Update(GlobalContext* globalCtx) { return; } + if (CVar_GetS32("gMessageTTS", 0) != 0) { + Message_TTS_Update(globalCtx); + } + bool isB_Held = CVar_GetS32("gFastText", 0) != 0 ? CHECK_BTN_ALL(input->cur.button, BTN_B) && !sTextboxSkipped : CHECK_BTN_ALL(input->press.button, BTN_B);