mirror of
https://github.com/HarbourMasters/Shipwright.git
synced 2025-08-20 05:13:39 -07:00
a11y: linux tts with espeak-ng (#5428)
* a11y: linux tts with espeak-ng * dlopen * Show setting on Linux
This commit is contained in:
parent
4c31f2bc88
commit
1e60c48e3a
7 changed files with 109 additions and 15 deletions
2
.github/workflows/apt-deps.txt
vendored
2
.github/workflows/apt-deps.txt
vendored
|
@ -1 +1 @@
|
||||||
libusb-dev libusb-1.0-0-dev libsdl2-dev libsdl2-net-dev libpng-dev libglew-dev nlohmann-json3-dev libtinyxml2-dev libspdlog-dev ninja-build
|
libusb-dev libusb-1.0-0-dev libsdl2-dev libsdl2-net-dev libpng-dev libglew-dev nlohmann-json3-dev libtinyxml2-dev libspdlog-dev libespeak-ng-dev ninja-build
|
||||||
|
|
|
@ -142,16 +142,21 @@ endif()
|
||||||
|
|
||||||
# handle Network removals
|
# handle Network removals
|
||||||
if (!BUILD_REMOTE_CONTROL)
|
if (!BUILD_REMOTE_CONTROL)
|
||||||
list(FILTER soh__ EXCLUDE REGEX "soh/Enhancements/crowd-control/*")
|
list(FILTER soh__ EXCLUDE REGEX "soh/Enhancements/crowd-control/")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# handle speechsynthesizer removals
|
# handle speechsynthesizer removals
|
||||||
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
if (CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||||
list(FILTER soh__ EXCLUDE REGEX "soh/Enhancements/speechsynthesizer/Darwin*")
|
list(FILTER soh__ EXCLUDE REGEX "soh/Enhancements/speechsynthesizer/Darwin")
|
||||||
elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin")
|
||||||
list(FILTER soh__ EXCLUDE REGEX "soh/Enhancements/speechsynthesizer/SAPI*")
|
list(FILTER soh__ EXCLUDE REGEX "soh/Enhancements/speechsynthesizer/SAPI")
|
||||||
else()
|
else()
|
||||||
list(FILTER soh__ EXCLUDE REGEX "soh/Enhancements/speechsynthesizer/(Darwin|SAPI).*")
|
list(FILTER soh__ EXCLUDE REGEX "soh/Enhancements/speechsynthesizer/(Darwin|SAPI)")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_library(ESPEAK espeak-ng)
|
||||||
|
if (NOT ESPEAK)
|
||||||
|
list(FILTER soh__ EXCLUDE REGEX "soh/Enhancements/speechsynthesizer/ESpeak")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# soh/Extractor {{{
|
# soh/Extractor {{{
|
||||||
|
@ -177,12 +182,12 @@ file(GLOB_RECURSE src__ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "src/*.c" "src/*.h"
|
||||||
set_source_files_properties(${src__} PROPERTIES COMPILE_OPTIONS "${WARNING_OVERRIDE}")
|
set_source_files_properties(${src__} PROPERTIES COMPILE_OPTIONS "${WARNING_OVERRIDE}")
|
||||||
|
|
||||||
list(APPEND src__ ${CMAKE_CURRENT_SOURCE_DIR}/Resource.rc)
|
list(APPEND src__ ${CMAKE_CURRENT_SOURCE_DIR}/Resource.rc)
|
||||||
list(FILTER src__ EXCLUDE REGEX "src/dmadata/*")
|
list(FILTER src__ EXCLUDE REGEX "src/dmadata/")
|
||||||
list(FILTER src__ EXCLUDE REGEX "src/elf_message/*")
|
list(FILTER src__ EXCLUDE REGEX "src/elf_message/")
|
||||||
list(FILTER src__ EXCLUDE REGEX "src/libultra/io/*")
|
list(FILTER src__ EXCLUDE REGEX "src/libultra/io/")
|
||||||
list(FILTER src__ EXCLUDE REGEX "src/libultra/libc/*")
|
list(FILTER src__ EXCLUDE REGEX "src/libultra/libc/")
|
||||||
list(FILTER src__ EXCLUDE REGEX "src/libultra/os/*")
|
list(FILTER src__ EXCLUDE REGEX "src/libultra/os/")
|
||||||
list(FILTER src__ EXCLUDE REGEX "src/libultra/rmon/*")
|
list(FILTER src__ EXCLUDE REGEX "src/libultra/rmon/")
|
||||||
list(APPEND src__ "src/libultra/libc/sprintf.c")
|
list(APPEND src__ "src/libultra/libc/sprintf.c")
|
||||||
list(REMOVE_ITEM src__ "src/libultra/gu/cosf.c")
|
list(REMOVE_ITEM src__ "src/libultra/gu/cosf.c")
|
||||||
list(REMOVE_ITEM src__ "src/libultra/gu/lookat.c")
|
list(REMOVE_ITEM src__ "src/libultra/gu/lookat.c")
|
||||||
|
@ -292,6 +297,10 @@ if (BUILD_REMOTE_CONTROL)
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if (ESPEAK)
|
||||||
|
add_compile_definitions(ESPEAK=1)
|
||||||
|
endif()
|
||||||
|
|
||||||
target_include_directories(${PROJECT_NAME} PRIVATE assets
|
target_include_directories(${PROJECT_NAME} PRIVATE assets
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/include/
|
${CMAKE_CURRENT_SOURCE_DIR}/include/
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src/
|
${CMAKE_CURRENT_SOURCE_DIR}/src/
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
#include "ESpeakSpeechSynthesizer.h"
|
||||||
|
#include <dlfcn.h>
|
||||||
|
#include <libultraship/libultraship.h>
|
||||||
|
|
||||||
|
ESpeakSpeechSynthesizer::ESpeakSpeechSynthesizer() {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ESpeakSpeechSynthesizer::DoInit() {
|
||||||
|
void* espeak = dlopen("libespeak-ng.so", RTLD_LAZY | RTLD_LOCAL);
|
||||||
|
if (espeak != NULL) {
|
||||||
|
this->Initialize = (speak_Initialize)dlsym(espeak, "espeak_Initialize");
|
||||||
|
this->SetVoiceByProperties = (speak_SetVoiceByProperties)dlsym(espeak, "espeak_SetVoiceByProperties");
|
||||||
|
this->Synth = (speak_Synth)dlsym(espeak, "espeak_Synth");
|
||||||
|
this->Terminate = (speak_Terminate)dlsym(espeak, "espeak_Terminate");
|
||||||
|
if (this->Initialize == NULL || this->SetVoiceByProperties == NULL || this->Synth == NULL ||
|
||||||
|
this->Terminate == NULL) {
|
||||||
|
lusprintf(__FILE__, __LINE__, 2, "Failed to load espeak-ng");
|
||||||
|
dlclose(espeak);
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
this->espeak = espeak;
|
||||||
|
return this->Initialize(AUDIO_OUTPUT_PLAYBACK, 100, NULL, 0) != -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESpeakSpeechSynthesizer::DoUninitialize() {
|
||||||
|
if (this->espeak != NULL) {
|
||||||
|
this->Terminate();
|
||||||
|
dlclose(this->espeak);
|
||||||
|
this->espeak = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ESpeakSpeechSynthesizer::Speak(const char* text, const char* language) {
|
||||||
|
if (this->espeak == NULL) {
|
||||||
|
lusprintf(__FILE__, __LINE__, 2, "Spoken Text (%s): %s", language, text);
|
||||||
|
} else {
|
||||||
|
if (language != this->mLanguage) {
|
||||||
|
espeak_VOICE voice = { .languages = language };
|
||||||
|
if (this->SetVoiceByProperties(&voice)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this->mLanguage = language;
|
||||||
|
}
|
||||||
|
this->Synth(text, 100, 0, POS_CHARACTER, 0, espeakCHARS_UTF8, NULL, NULL);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "SpeechSynthesizer.h"
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include <espeak-ng/speak_lib.h>
|
||||||
|
|
||||||
|
// C23 typeof could help here
|
||||||
|
typedef ESPEAK_API int (*speak_Initialize)(espeak_AUDIO_OUTPUT output, int buflength, const char* path, int options);
|
||||||
|
typedef ESPEAK_API espeak_ERROR (*speak_Terminate)(void);
|
||||||
|
typedef ESPEAK_API espeak_ERROR (*speak_SetVoiceByProperties)(espeak_VOICE* voice_spec);
|
||||||
|
typedef ESPEAK_API espeak_ERROR (*speak_Synth)(const void* text, size_t size, unsigned int position,
|
||||||
|
espeak_POSITION_TYPE position_type, unsigned int end_position,
|
||||||
|
unsigned int flags, unsigned int* unique_identifier, void* user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ESpeakSpeechSynthesizer : public SpeechSynthesizer {
|
||||||
|
public:
|
||||||
|
ESpeakSpeechSynthesizer();
|
||||||
|
|
||||||
|
void Speak(const char* text, const char* language);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool DoInit(void);
|
||||||
|
void DoUninitialize(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const char* mLanguage = NULL;
|
||||||
|
void* espeak = NULL;
|
||||||
|
speak_Initialize Initialize = NULL;
|
||||||
|
speak_SetVoiceByProperties SetVoiceByProperties = NULL;
|
||||||
|
speak_Synth Synth = NULL;
|
||||||
|
speak_Terminate Terminate = NULL;
|
||||||
|
};
|
|
@ -35,6 +35,8 @@ class SpeechSynthesizer {
|
||||||
#include "SAPISpeechSynthesizer.h"
|
#include "SAPISpeechSynthesizer.h"
|
||||||
#elif defined(__APPLE__)
|
#elif defined(__APPLE__)
|
||||||
#include "DarwinSpeechSynthesizer.h"
|
#include "DarwinSpeechSynthesizer.h"
|
||||||
|
#elif ESPEAK
|
||||||
|
#include "ESpeakSpeechSynthesizer.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "SpeechLogger.h"
|
#include "SpeechLogger.h"
|
||||||
|
|
|
@ -1229,14 +1229,14 @@ extern "C" void InitOTR() {
|
||||||
ActorDB::Instance = new ActorDB();
|
ActorDB::Instance = new ActorDB();
|
||||||
#ifdef __APPLE__
|
#ifdef __APPLE__
|
||||||
SpeechSynthesizer::Instance = new DarwinSpeechSynthesizer();
|
SpeechSynthesizer::Instance = new DarwinSpeechSynthesizer();
|
||||||
SpeechSynthesizer::Instance->Init();
|
|
||||||
#elif defined(_WIN32)
|
#elif defined(_WIN32)
|
||||||
SpeechSynthesizer::Instance = new SAPISpeechSynthesizer();
|
SpeechSynthesizer::Instance = new SAPISpeechSynthesizer();
|
||||||
SpeechSynthesizer::Instance->Init();
|
#elif ESPEAK
|
||||||
|
SpeechSynthesizer::Instance = new ESpeakSpeechSynthesizer();
|
||||||
#else
|
#else
|
||||||
SpeechSynthesizer::Instance = new SpeechLogger();
|
SpeechSynthesizer::Instance = new SpeechLogger();
|
||||||
SpeechSynthesizer::Instance->Init();
|
|
||||||
#endif
|
#endif
|
||||||
|
SpeechSynthesizer::Instance->Init();
|
||||||
|
|
||||||
#ifdef ENABLE_REMOTE_CONTROL
|
#ifdef ENABLE_REMOTE_CONTROL
|
||||||
CrowdControl::Instance = new CrowdControl();
|
CrowdControl::Instance = new CrowdControl();
|
||||||
|
|
|
@ -190,7 +190,7 @@ void SohMenu::AddMenuSettings() {
|
||||||
.ComboMap(languages)
|
.ComboMap(languages)
|
||||||
.DefaultIndex(LANGUAGE_ENG));
|
.DefaultIndex(LANGUAGE_ENG));
|
||||||
AddWidget(path, "Accessibility", WIDGET_SEPARATOR_TEXT);
|
AddWidget(path, "Accessibility", WIDGET_SEPARATOR_TEXT);
|
||||||
#if defined(_WIN32) || defined(__APPLE__)
|
#if defined(_WIN32) || defined(__APPLE__) || defined(ESPEAK)
|
||||||
AddWidget(path, "Text to Speech", WIDGET_CVAR_CHECKBOX)
|
AddWidget(path, "Text to Speech", WIDGET_CVAR_CHECKBOX)
|
||||||
.CVar(CVAR_SETTING("A11yTTS"))
|
.CVar(CVAR_SETTING("A11yTTS"))
|
||||||
.RaceDisable(false)
|
.RaceDisable(false)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue