diff --git a/accessibility.h b/accessibility.h index df965df2bf..c893d9a2e9 100644 --- a/accessibility.h +++ b/accessibility.h @@ -32,6 +32,15 @@ #include "configuration.h" + +enum accessibility_narrator_synthesizer +{ + ACCESSIBILITY_NARRATOR_SYNTHESIZER_NATIVE = 0, + ACCESSIBILITY_NARRATOR_SYNTHESIZER_SPEACH_DISPATCHER, + ACCESSIBILITY_NARRATOR_SYNTHESIZER_ESPEAK, + ACCESSIBILITY_NARRATOR_SYNTHESIZER_LAST +}; + typedef struct { int ai_service_auto; diff --git a/config.def.h b/config.def.h index c7710481e8..505f8419f6 100644 --- a/config.def.h +++ b/config.def.h @@ -180,6 +180,8 @@ #define DEFAULT_ACCESSIBILITY_ENABLE false +#define DEFAULT_ACCESSIBILITY_NARRATOR_SYNTHESIZER 0 + #define DEFAULT_ACCESSIBILITY_NARRATOR_SPEECH_SPEED 5 #define DEFAULT_DRIVER_SWITCH_ENABLE true diff --git a/configuration.c b/configuration.c index b4d25aceb6..343850fc5e 100644 --- a/configuration.c +++ b/configuration.c @@ -2621,6 +2621,7 @@ static struct config_uint_setting *populate_settings_uint( SETTING_UINT("cheevos_appearance_anchor", &settings->uints.cheevos_appearance_anchor, true, DEFAULT_CHEEVOS_APPEARANCE_ANCHOR, false); SETTING_UINT("cheevos_visibility_summary", &settings->uints.cheevos_visibility_summary, true, DEFAULT_CHEEVOS_VISIBILITY_SUMMARY, false); #endif + SETTING_UINT("accessibility_narrator_synthesizer", &settings->uints.accessibility_narrator_synthesizer, true, DEFAULT_ACCESSIBILITY_NARRATOR_SYNTHESIZER, false); SETTING_UINT("accessibility_narrator_speech_speed", &settings->uints.accessibility_narrator_speech_speed, true, DEFAULT_ACCESSIBILITY_NARRATOR_SPEECH_SPEED, false); SETTING_UINT("ai_service_mode", &settings->uints.ai_service_mode, true, DEFAULT_AI_SERVICE_MODE, false); SETTING_UINT("ai_service_target_lang", &settings->uints.ai_service_target_lang, true, 0, false); diff --git a/configuration.h b/configuration.h index 535cee1763..32887622cf 100644 --- a/configuration.h +++ b/configuration.h @@ -279,6 +279,7 @@ typedef struct settings #endif /* Accessibility */ + unsigned accessibility_narrator_synthesizer; unsigned accessibility_narrator_speech_speed; unsigned menu_timedate_style; diff --git a/frontend/drivers/platform_unix.c b/frontend/drivers/platform_unix.c index e1db1da096..8af12792a4 100644 --- a/frontend/drivers/platform_unix.c +++ b/frontend/drivers/platform_unix.c @@ -51,6 +51,10 @@ #include #include +#if (defined(__linux__) || defined(__HAIKU__) || defined(__unix__)) && !defined(ANDROID) +#include "accessibility.h" +#endif + #ifdef HAVE_CONFIG_H #include "../../config.h" #endif @@ -2899,7 +2903,8 @@ static const char* accessibility_unix_language_code(const char* language) string_is_equal(language, "ta") || string_is_equal(language, "te") || string_is_equal(language, "ur") || - string_is_equal(language, "cy") + string_is_equal(language, "cy") || + string_is_equal(language, "ca") ) return language; else if ( @@ -2908,19 +2913,16 @@ static const char* accessibility_unix_language_code(const char* language) ) return "nb"; else if (string_is_equal(language, "en_gb")) - return "en-gb"; - else if ( - string_is_equal(language, "ca") || - string_is_equal(language, "ca_ES@valencia") - ) - return "ca"; + return "en-GB"; + else if (string_is_equal(language, "ca_ES@valencia")) + return "ca-VA"; else if ( string_is_equal(language, "pt_pt") || string_is_equal(language, "pt") ) return "pt"; else if (string_is_equal(language, "pt_bt")) - return "pt-br"; + return "pt-BR"; else if ( string_is_equal(language, "zh") || string_is_equal(language, "zh_cn") || @@ -2938,27 +2940,69 @@ static const char* accessibility_unix_language_code(const char* language) static bool accessibility_speak_unix(int speed, const char* speak_text, int priority) { + unsigned synthesizer; int pid; const char* language = accessibility_unix_language_code(get_user_language_iso639_1(true)); char* voice_out = (char*)malloc(3 + strlen(language)); char* speed_out = (char*)malloc(3 + 3); const char* speeds[10] = {"80", "100", "125", "150", "170", "210", "260", "310", "380", "450"}; + char* executable = (char*)malloc(16); + + settings_t *settings = config_get_ptr(); + synthesizer = settings->uints.accessibility_narrator_synthesizer; + + switch (synthesizer) + { + case ACCESSIBILITY_NARRATOR_SYNTHESIZER_SPEACH_DISPATCHER: + { + strlcpy(executable, "spd-say", 8); + speeds[0] = "-99"; + speeds[1] = "-75"; + speeds[2] = "-50"; + speeds[3] = "-25"; + speeds[4] = "0"; + speeds[5] = "20"; + speeds[6] = "40"; + speeds[7] = "60"; + speeds[8] = "80"; + speeds[9] = "100"; + + voice_out[0] = '-'; + voice_out[1] = 'l'; + voice_out[2] = '\0'; + strlcat(voice_out, language, sizeof(voice_out)); + + speed_out[0] = '-'; + speed_out[1] = 'r'; + speed_out[2] = '\0'; + strlcat(speed_out, speeds[speed-1], sizeof(speed_out)); + + break; + } + case ACCESSIBILITY_NARRATOR_SYNTHESIZER_ESPEAK: + default: + { + strlcpy(executable, "espeak", 7); + + voice_out[0] = '-'; + voice_out[1] = 'v'; + voice_out[2] = '\0'; + strlcat(voice_out, language, sizeof(voice_out)); + + speed_out[0] = '-'; + speed_out[1] = 's'; + speed_out[2] = '\0'; + strlcat(speed_out, speeds[speed-1], sizeof(speed_out)); + + break; + } + } if (speed < 1) speed = 1; else if (speed > 10) speed = 10; - voice_out[0] = '-'; - voice_out[1] = 'v'; - voice_out[2] = '\0'; - strlcat(voice_out, language, 3 + strlen(language)); - - speed_out[0] = '-'; - speed_out[1] = 's'; - speed_out[2] = '\0'; - strlcat(speed_out, speeds[speed-1], 6); - if (priority < 10 && speak_pid > 0) { /* check if old pid is running */ @@ -2968,7 +3012,7 @@ static bool accessibility_speak_unix(int speed, if (speak_pid > 0) { - /* Kill the running espeak */ + /* Kill the running TTS Engine */ kill(speak_pid, SIGTERM); speak_pid = 0; } @@ -2978,19 +3022,20 @@ static bool accessibility_speak_unix(int speed, { case 0: { - /* child process: replace process with the espeak command */ - char* cmd[] = { (char*) "espeak", NULL, NULL, NULL, NULL }; + /* child process: replace process with the TTS command */ + char* cmd[] = { NULL, NULL, NULL, NULL, NULL }; + cmd[0] = executable; cmd[1] = voice_out; cmd[2] = speed_out; cmd[3] = (char*)speak_text; - execvp("espeak", cmd); + execvp(executable, cmd); - RARCH_WARN("Could not execute espeak.\n"); + RARCH_WARN("Could not execute TTS Engine.\n"); /* Prevent interfere with the parent process */ _exit(EXIT_FAILURE); } case -1: - RARCH_ERR("Could not fork for espeak.\n"); + RARCH_ERR("Could not fork for the TTS process.\n"); default: { /* parent process */ @@ -3007,6 +3052,8 @@ end: free(voice_out); if (speed_out) free(speed_out); + if (executable) + free(executable); return true; } #endif diff --git a/intl/msg_hash_lbl.h b/intl/msg_hash_lbl.h index b83177539e..5c307832b9 100644 --- a/intl/msg_hash_lbl.h +++ b/intl/msg_hash_lbl.h @@ -2175,7 +2175,7 @@ MSG_HASH( MSG_HASH( MENU_ENUM_LABEL_INPUT_REMAP_SORT_BY_CONTROLLER_ENABLE, "input_remap_sort_by_controller_enable" - ) + ) MSG_HASH( MENU_ENUM_LABEL_INPUT_SETTINGS, "input_settings" @@ -6580,6 +6580,10 @@ MSG_HASH( MENU_ENUM_LABEL_ACCESSIBILITY_ENABLED, "accessibility_enabled" ) +MSG_HASH( + MENU_ENUM_LABEL_ACCESSIBILITY_NARRATOR_SYNTHESIZER, + "accessibility_narrator_synthesizer" + ) MSG_HASH( MENU_ENUM_LABEL_ACCESSIBILITY_NARRATOR_SPEECH_SPEED, "accessibility_narrator_speech_speed" diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 8fd00974cb..8270b2e179 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -7235,6 +7235,14 @@ MSG_HASH( MENU_ENUM_SUBLABEL_ACCESSIBILITY_ENABLED, "Enable Text-to-Speech to aid in menu navigation." ) +MSG_HASH( + MENU_ENUM_LABEL_VALUE_ACCESSIBILITY_NARRATOR_SYNTHESIZER, + "Text-to-Speech synthesizer" + ) +MSG_HASH( + MENU_ENUM_SUBLABEL_ACCESSIBILITY_NARRATOR_SYNTHESIZER, + "The engine of the Text-to-Speech synthesis." + ) MSG_HASH( MENU_ENUM_LABEL_VALUE_ACCESSIBILITY_NARRATOR_SPEECH_SPEED, "Text-to-Speech Speed" diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index 29b36c6cd0..a6bd3b0a29 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -231,6 +231,7 @@ DEFAULT_SUBLABEL_MACRO(menu_action_sublabel_setting_audio_mixer_stream_volume, MENU_ENUM_SUBLABEL_MIXER_ACTION_VOLUME) #endif DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_accessibility_enabled, MENU_ENUM_SUBLABEL_ACCESSIBILITY_ENABLED) +DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_accessibility_narrator_synthesizer, MENU_ENUM_SUBLABEL_ACCESSIBILITY_NARRATOR_SYNTHESIZER) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_accessibility_narrator_speech_speed, MENU_ENUM_SUBLABEL_ACCESSIBILITY_NARRATOR_SPEECH_SPEED) DEFAULT_SUBLABEL_MACRO(action_bind_sublabel_load_config, MENU_ENUM_SUBLABEL_CONFIGURATIONS) @@ -5342,6 +5343,9 @@ int menu_cbs_init_bind_sublabel(menu_file_list_cbs_t *cbs, case MENU_ENUM_LABEL_ACCESSIBILITY_ENABLED: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_accessibility_enabled); break; + case MENU_ENUM_LABEL_ACCESSIBILITY_NARRATOR_SYNTHESIZER: + BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_accessibility_narrator_synthesizer); + break; case MENU_ENUM_LABEL_ACCESSIBILITY_NARRATOR_SPEECH_SPEED: BIND_ACTION_SUBLABEL(cbs, action_bind_sublabel_accessibility_narrator_speech_speed); break; diff --git a/menu/menu_displaylist.c b/menu/menu_displaylist.c index 1019c944b1..27c2d4a184 100644 --- a/menu/menu_displaylist.c +++ b/menu/menu_displaylist.c @@ -8296,6 +8296,7 @@ unsigned menu_displaylist_build_list( bool accessibility_enable = settings->bools.accessibility_enable; menu_displaylist_build_info_selective_t build_list[] = { {MENU_ENUM_LABEL_ACCESSIBILITY_ENABLED, PARSE_ONLY_BOOL, true }, + {MENU_ENUM_LABEL_ACCESSIBILITY_NARRATOR_SYNTHESIZER, PARSE_ONLY_UINT, false }, {MENU_ENUM_LABEL_ACCESSIBILITY_NARRATOR_SPEECH_SPEED, PARSE_ONLY_UINT, false }, {MENU_ENUM_LABEL_AI_SERVICE_SETTINGS, PARSE_ACTION, true }, }; @@ -8304,6 +8305,9 @@ unsigned menu_displaylist_build_list( { switch (build_list[i].enum_idx) { +#if (defined(__linux__) || defined(__unix__)) && !defined(ANDROID) + case MENU_ENUM_LABEL_ACCESSIBILITY_NARRATOR_SYNTHESIZER: +#endif case MENU_ENUM_LABEL_ACCESSIBILITY_NARRATOR_SPEECH_SPEED: if (accessibility_enable) build_list[i].checked = true; diff --git a/menu/menu_setting.c b/menu/menu_setting.c index ffc3617b09..f7ac494678 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -31,6 +31,12 @@ #include +#ifdef HAVE_ACCESSIBILITY +#if (defined(__linux__) || defined(__HAIKU__) || defined(__unix__)) && !defined(ANDROID) +#include "accessibility.h" +#endif +#endif + #ifdef HAVE_CONFIG_H #include "../config.h" #endif @@ -3175,6 +3181,29 @@ static size_t setting_get_string_representation_uint_keyboard_gamepad_mapping_ty } #endif +#ifdef HAVE_ACCESSIBILITY +#if (defined(__linux__) || defined(__HAIKU__) || defined(__unix__)) && !defined(ANDROID) +static size_t setting_get_string_representation_uint_accessibility_narrator_synthesizer( + rarch_setting_t *setting, char *s, size_t len) +{ + if (!setting) + return 0; + switch(*setting->value.target.unsigned_integer) + { + case ACCESSIBILITY_NARRATOR_SYNTHESIZER_NATIVE: + return strlcpy(s, "native", len); + case ACCESSIBILITY_NARRATOR_SYNTHESIZER_SPEACH_DISPATCHER: + return strlcpy(s, "Speech Dispatcher", len); + case ACCESSIBILITY_NARRATOR_SYNTHESIZER_ESPEAK: + return strlcpy(s, "eSpeak", len); + case ACCESSIBILITY_NARRATOR_SYNTHESIZER_LAST: + return 0; + } + return 0; +} +#endif +#endif + #ifdef HAVE_TRANSLATE static size_t setting_get_string_representation_uint_ai_service_mode( rarch_setting_t *setting, char *s, size_t len) @@ -12099,7 +12128,7 @@ static bool setting_append_list( parent_group, general_write_handler, general_read_handler); - menu_settings_list_current_add_range(list, list_info, + menu_settings_list_current_add_range(list, list_info, 0, cheat_manager_get_state_search_size(cheat_manager_state.working_cheat.memory_search_size), 1, true, true); (*list)[list_info->index - 1].get_string_representation = &setting_get_string_representation_hex_and_uint; SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ALLOW_INPUT); @@ -12192,7 +12221,7 @@ static bool setting_append_list( parent_group, general_write_handler, general_read_handler); - menu_settings_list_current_add_range(list, list_info, + menu_settings_list_current_add_range(list, list_info, 0, cheat_manager_get_state_search_size(cheat_manager_state.working_cheat.memory_search_size), 1, true, true); (*list)[list_info->index - 1].get_string_representation = &setting_get_string_representation_hex_and_uint; SETTINGS_DATA_LIST_CURRENT_ADD_FLAGS(list, list_info, SD_FLAG_ALLOW_INPUT); @@ -20542,6 +20571,26 @@ static bool setting_append_list( (*list)[list_info->index - 1].action_left = setting_bool_action_left_with_refresh; (*list)[list_info->index - 1].action_right = setting_bool_action_right_with_refresh; +#ifdef HAVE_ACCESSIBILITY +#if (defined(__linux__) || defined(__HAIKU__) || defined(__unix__)) && !defined(ANDROID) + CONFIG_UINT( + list, list_info, + &settings->uints.accessibility_narrator_synthesizer, + MENU_ENUM_LABEL_ACCESSIBILITY_NARRATOR_SYNTHESIZER, + MENU_ENUM_LABEL_VALUE_ACCESSIBILITY_NARRATOR_SYNTHESIZER, + DEFAULT_ACCESSIBILITY_NARRATOR_SYNTHESIZER, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler); + (*list)[list_info->index - 1].get_string_representation = + &setting_get_string_representation_uint_accessibility_narrator_synthesizer; + (*list)[list_info->index - 1].action_ok = &setting_action_ok_uint; + menu_settings_list_current_add_range(list, list_info, ACCESSIBILITY_NARRATOR_SYNTHESIZER_NATIVE, (ACCESSIBILITY_NARRATOR_SYNTHESIZER_LAST - 1), 1, true, true); +#endif +#endif + CONFIG_UINT( list, list_info, &settings->uints.accessibility_narrator_speech_speed, diff --git a/msg_hash.h b/msg_hash.h index 11a8ade127..f5772beae2 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -2945,6 +2945,7 @@ enum msg_hash_enums MENU_LABEL(INPUT_HAPTIC_FEEDBACK_SETTINGS), MENU_LABEL(ACCESSIBILITY_SETTINGS), MENU_LABEL(ACCESSIBILITY_ENABLED), + MENU_LABEL(ACCESSIBILITY_NARRATOR_SYNTHESIZER), MENU_LABEL(ACCESSIBILITY_NARRATOR_SPEECH_SPEED), MENU_LABEL(AI_SERVICE_SETTINGS), MENU_LABEL(AI_SERVICE_MODE),