Added in accessibility.

This commit is contained in:
Barry Rowe 2019-11-15 19:08:49 -08:00 committed by Barry Rowe
parent 203a80aeaa
commit 1e6e7a698f
9 changed files with 829 additions and 16 deletions

View File

@ -55,6 +55,7 @@ static enum action_iterate_type action_iterate_type(const char *label)
return ITERATE_TYPE_DEFAULT;
}
/**
* menu_iterate:
* @input : input sample for this frame
@ -68,6 +69,8 @@ static enum action_iterate_type action_iterate_type(const char *label)
**/
int generic_menu_iterate(void *data, void *userdata, enum menu_action action)
{
static enum action_iterate_type last_iterate_type = ITERATE_TYPE_DEFAULT;
enum action_iterate_type iterate_type;
unsigned file_type = 0;
int ret = 0;
@ -91,12 +94,17 @@ int generic_menu_iterate(void *data, void *userdata, enum menu_action action)
{
BIT64_SET(menu->state, MENU_STATE_RENDER_FRAMEBUFFER);
}
switch (iterate_type)
{
case ITERATE_TYPE_HELP:
ret = menu_dialog_iterate(
menu->menu_state_msg, sizeof(menu->menu_state_msg), label);
if (iterate_type != last_iterate_type && is_accessibility_enabled())
{
accessibility_speak(menu->menu_state_msg);
}
BIT64_SET(menu->state, MENU_STATE_RENDER_MESSAGEBOX);
BIT64_SET(menu->state, MENU_STATE_POST_ITERATE);
if (ret == 1 || action == MENU_ACTION_OK)
@ -138,8 +146,24 @@ int generic_menu_iterate(void *data, void *userdata, enum menu_action action)
: NULL;
if (cbs && cbs->enum_idx != MSG_UNKNOWN)
{
ret = menu_hash_get_help_enum(cbs->enum_idx,
menu->menu_state_msg, sizeof(menu->menu_state_msg));
if (iterate_type != last_iterate_type && is_accessibility_enabled())
{
if (strcmp(menu->menu_state_msg, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_NO_INFORMATION_AVAILABLE))==0)
{
char current_sublabel[255];
get_current_menu_sublabel(current_sublabel);
if (strcmp(current_sublabel, "")==0)
accessibility_speak(menu->menu_state_msg);
else
accessibility_speak(current_sublabel);
}
else
accessibility_speak(menu->menu_state_msg);
}
}
else
{
unsigned type = 0;
@ -254,6 +278,12 @@ int generic_menu_iterate(void *data, void *userdata, enum menu_action action)
break;
}
if ((last_iterate_type == ITERATE_TYPE_HELP || last_iterate_type == ITERATE_TYPE_INFO) && last_iterate_type != iterate_type && is_accessibility_enabled())
{
accessibility_speak("Closed dialog.");
}
last_iterate_type = iterate_type;
BIT64_SET(menu->state, MENU_STATE_BLIT);
if (BIT64_GET(menu->state, MENU_STATE_POP_STACK))
@ -383,5 +413,59 @@ int generic_menu_entry_action(
}
}
if (action != 0 && is_accessibility_enabled() && !is_input_keyboard_display_on())
{
char current_label[255];
char current_value[255];
char title_name[255];
char speak_string[512];
strcpy(title_name, "");
strcpy(current_label, "");
get_current_menu_value(current_value);
switch (action)
{
case MENU_ACTION_INFO:
break;
case MENU_ACTION_OK:
case MENU_ACTION_LEFT:
case MENU_ACTION_RIGHT:
case MENU_ACTION_CANCEL:
menu_entries_get_title(title_name, 255);
case MENU_ACTION_UP:
case MENU_ACTION_DOWN:
case MENU_ACTION_SCROLL_UP:
case MENU_ACTION_SCROLL_DOWN:
get_current_menu_label(current_label);
break;
case MENU_ACTION_START:
case MENU_ACTION_SELECT:
case MENU_ACTION_SEARCH:
get_current_menu_label(current_label);
case MENU_ACTION_SCAN:
default:
break;
}
{
strcpy(speak_string, "");
if (strcmp(title_name, "") != 0)
{
strcpy(speak_string, title_name);
strcat(speak_string, " ");
}
strcat(speak_string, current_label);
if (strcmp(current_value, "...")!=0)
{
strcat(speak_string, " ");
strcat(speak_string, current_value);
}
if (strcmp(speak_string, "") != 0)
{
accessibility_speak(speak_string);
}
}
}
return ret;
}

View File

@ -4114,7 +4114,7 @@ void menu_subsystem_populate(const struct retro_subsystem_info* subsystem, menu_
RARCH_WARN("Menu subsystem entry: Description label truncated.\n");
}
}
strlcpy(s, tmp, sizeof(s));
}
}
@ -4128,3 +4128,43 @@ void menu_subsystem_populate(const struct retro_subsystem_info* subsystem, menu_
}
}
}
void get_current_menu_value(char* retstr)
{
const char* entry_label;
menu_entry_t entry;
menu_driver_selection_ptr = menu_navigation_get_selection();
menu_entry_init(&entry);
menu_entry_get(&entry, 0, menu_navigation_get_selection(), NULL, true);
menu_entry_get_value(&entry, &entry_label);
strcpy(retstr, entry_label);
}
void get_current_menu_label(char* retstr)
{
const char* entry_label;
menu_entry_t entry;
menu_driver_selection_ptr = menu_navigation_get_selection();
menu_entry_init(&entry);
menu_entry_get(&entry, 0, menu_navigation_get_selection(), NULL, true);
menu_entry_get_rich_label(&entry, &entry_label);
strcpy(retstr, entry_label);
}
void get_current_menu_sublabel(char* retstr)
{
const char* entry_sublabel;
menu_entry_t entry;
menu_driver_selection_ptr = menu_navigation_get_selection();
menu_entry_init(&entry);
menu_entry_get(&entry, 0, menu_navigation_get_selection(), NULL, true);
menu_entry_get_sublabel(&entry, &entry_sublabel);
strcpy(retstr, entry_sublabel);
}

View File

@ -281,6 +281,10 @@ int menu_entry_action(
void menu_entry_init(menu_entry_t *entry);
void get_current_menu_value(char* retstr);
void get_current_menu_label(char* retstr);
void get_current_menu_sublabel(char* retstr);
RETRO_END_DECLS
#endif

View File

@ -398,7 +398,6 @@ static int setting_generic_action_ok_linefeed(rarch_setting_t *setting, bool wra
if (!menu_input_dialog_start(&line))
return -1;
return 0;
}

View File

@ -329,6 +329,10 @@ void menu_widgets_msg_queue_push(
{
menu_widget_msg_t* msg_widget = NULL;
if (is_accessibility_enabled())
{
accessibility_speak_priority((char*)msg, 0);
}
if (fifo_write_avail(msg_queue) > 0)
{
/* Get current msg if it exists */
@ -2489,7 +2493,7 @@ void menu_widgets_set_libretro_message(const char *msg, unsigned duration)
menu_timer_ctx_entry_t timer;
strlcpy(libretro_message, msg, LIBRETRO_MESSAGE_SIZE);
libretro_message_alpha = DEFAULT_BACKDROP;
/* Kill and restart the timer / animation */

View File

@ -102,6 +102,77 @@ int menu_hash_get_help_enum(enum msg_hash_enums msg, char *s, size_t len)
#endif
}
const char *get_user_language_iso639_1(bool limit)
{
char *voice;
voice = "en";
switch (uint_user_language)
{
case RETRO_LANGUAGE_FRENCH:
voice = "fr";
break;
case RETRO_LANGUAGE_GERMAN:
voice = "de";
break;
case RETRO_LANGUAGE_SPANISH:
voice = "es";
break;
case RETRO_LANGUAGE_ITALIAN:
voice = "it";
break;
case RETRO_LANGUAGE_PORTUGUESE_BRAZIL:
if (limit)
voice = "pt";
else
voice = "pt_br";
break;
case RETRO_LANGUAGE_PORTUGUESE_PORTUGAL:
if (limit)
voice = "pt";
else
voice = "pt_pt";
break;
case RETRO_LANGUAGE_DUTCH:
voice = "nl";
break;
case RETRO_LANGUAGE_ESPERANTO:
voice = "eo";
break;
case RETRO_LANGUAGE_POLISH:
voice = "pl";
break;
case RETRO_LANGUAGE_JAPANESE:
voice = "ja";
break;
case RETRO_LANGUAGE_KOREAN:
voice = "ko";
break;
case RETRO_LANGUAGE_VIETNAMESE:
voice = "vi";
break;
case RETRO_LANGUAGE_CHINESE_SIMPLIFIED:
voice = "zh";
break;
case RETRO_LANGUAGE_CHINESE_TRADITIONAL:
voice = "zh";
break;
case RETRO_LANGUAGE_ARABIC:
voice = "ar";
break;
case RETRO_LANGUAGE_GREEK:
voice = "el";
break;
case RETRO_LANGUAGE_TURKISH:
voice = "tr";
break;
case RETRO_LANGUAGE_RUSSIAN:
voice = "ru";
break;
}
return voice;
}
const char *msg_hash_to_str(enum msg_hash_enums msg)
{
const char *ret = NULL;

View File

@ -20,7 +20,7 @@
#include <stdint.h>
#include <stddef.h>
#include <boolean.h>
#include <retro_common_api.h>
#include "input/input_defines.h"
@ -2987,6 +2987,8 @@ void msg_hash_set_uint(enum msg_hash_action type, unsigned val);
uint32_t msg_hash_calculate(const char *s);
const char *get_user_language_iso639_1(bool limit);
RETRO_END_DECLS
#endif

View File

@ -37,6 +37,7 @@
#if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)
#include <objbase.h>
#include <process.h>
#endif
#include <stdio.h>
@ -808,7 +809,8 @@ enum
RA_OPT_MAX_FRAMES,
RA_OPT_MAX_FRAMES_SCREENSHOT,
RA_OPT_MAX_FRAMES_SCREENSHOT_PATH,
RA_OPT_SET_SHADER
RA_OPT_SET_SHADER,
RA_OPT_ACCESSIBILITY,
};
enum runloop_state
@ -2309,6 +2311,10 @@ static bool menu_driver_is_binding = false;
* it will be closed; if the menu was not running, it will be opened */
static bool menu_driver_toggled = false;
/* Is text-to-speech accessibility turned on? */
static bool accessibility_enabled = false;
#ifdef HAVE_LIBNX
#define LIBNX_SWKBD_LIMIT 500 /* enforced by HOS */
extern u32 __nx_applet_type;
@ -2405,6 +2411,11 @@ bool menu_input_dialog_start_search(void)
sizeof(menu_input_dialog_keyboard_label));
input_keyboard_ctl(RARCH_INPUT_KEYBOARD_CTL_LINE_FREE, NULL);
if (is_accessibility_enabled())
{
accessibility_speak((char*) msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SEARCH));
}
menu_input_dialog_keyboard_buffer =
input_keyboard_start_line(menu, menu_input_search_cb);
@ -2434,6 +2445,8 @@ bool menu_input_dialog_start(menu_input_ctx_line_t *line)
input_keyboard_ctl(RARCH_INPUT_KEYBOARD_CTL_LINE_FREE, NULL);
accessibility_speak("Keyboard input:");
menu_input_dialog_keyboard_buffer =
input_keyboard_start_line(menu, line->cb);
@ -2475,7 +2488,6 @@ bool menu_input_dialog_get_display_kb(void)
/* In case a previous "Enter" press closed the keyboard */
if (!menu_input_dialog_keyboard_display)
break;
if (buf[i] == '\n' || buf[i] == '\0')
input_keyboard_event(true, '\n', '\n', 0, RETRO_DEVICE_KEYBOARD);
else
@ -4071,6 +4083,7 @@ static void handle_translation_cb(
int start = -1;
char* found_string = NULL;
char* error_string = NULL;
char* text_string = NULL;
int curr_state = 0;
RARCH_LOG("RESULT FROM AI SERVICE...\n");
@ -4098,7 +4111,6 @@ static void handle_translation_cb(
{
found_string = (char*)malloc(i-start+1);
strlcpy(found_string, body_copy+start+1, i-start);
if (curr_state == 1)/*image*/
{
raw_image_file_data = (char*)unbase64(found_string,
@ -4115,6 +4127,12 @@ static void handle_translation_cb(
}
#endif
else if (curr_state == 3)
{
text_string = (char*)malloc(i-start+1);
strlcpy(text_string, body_copy+start+1, i-start);
curr_state = 0;
}
else if (curr_state == 4)
{
error_string = (char*)malloc(i-start+1);
strlcpy(error_string, body_copy+start+1, i-start);
@ -4130,11 +4148,16 @@ static void handle_translation_cb(
curr_state = 2;
free(found_string);
}
else if (string_is_equal(found_string, "error"))
else if (string_is_equal(found_string, "text"))
{
curr_state = 3;
free(found_string);
}
else if (string_is_equal(found_string, "error"))
{
curr_state = 4;
free(found_string);
}
else
{
curr_state = 0;
@ -4149,6 +4172,7 @@ static void handle_translation_cb(
if (string_is_equal(error_string, "No text found."))
{
RARCH_LOG("No text found...\n");
strcpy(text_string, error_string);
#ifdef HAVE_MENU_WIDGETS
if (menu_widgets_paused)
{
@ -4159,7 +4183,7 @@ static void handle_translation_cb(
#endif
}
if (!raw_image_file_data && !raw_sound_data)
if (!raw_image_file_data && !raw_sound_data && !text_string)
{
error = "Invalid JSON body.";
goto finish;
@ -4372,7 +4396,7 @@ static void handle_translation_cb(
return;
params.volume = 1.0f;
params.slot_selection_type = AUDIO_MIXER_SLOT_SELECTION_AUTOMATIC; /* user->slot_selection_type; */
params.slot_selection_type = AUDIO_MIXER_SLOT_SELECTION_MANUAL; /* user->slot_selection_type; */
params.slot_selection_idx = 10;
params.stream_type = AUDIO_STREAM_TYPE_SYSTEM; /* user->stream_type; */
params.type = AUDIO_MIXER_TYPE_WAV;
@ -4392,6 +4416,11 @@ static void handle_translation_cb(
}
#endif
if (text_string && is_accessibility_enabled())
{
accessibility_speak(text_string);
}
finish:
if (error)
RARCH_ERR("%s: %s\n", msg_hash_to_str(MSG_DOWNLOAD_FAILED), error);
@ -4417,10 +4446,28 @@ finish:
free(scaler);
if (error_string)
free(error_string);
if (text_string)
free(text_string);
if (raw_output_data)
free(raw_output_data);
}
bool is_ai_service_speech_running()
{
enum audio_mixer_state res = audio_driver_mixer_get_stream_state(10);
RARCH_LOG("TTT %d\n", res);
if (res == AUDIO_STREAM_STATE_NONE || res == AUDIO_STREAM_STATE_STOPPED)
return false;
return true;
}
bool ai_service_speech_stop()
{
audio_driver_mixer_stop_stream(10);
audio_driver_mixer_remove_stream(10);
return false;
}
static const char *ai_service_get_str(enum translation_lang id)
{
switch (id)
@ -4880,6 +4927,8 @@ static bool run_translation_service(void)
else if (settings->uints.ai_service_mode == 1)
mode_chr = "sound,wav";
else if (settings->uints.ai_service_mode == 2)
mode_chr = "text";
else if (settings->uints.ai_service_mode == 3)
{
if (use_overlay)
mode_chr = "image,png,png-a,sound,wav";
@ -4887,6 +4936,7 @@ static bool run_translation_service(void)
mode_chr = "image,png,sound,wav";
}
snprintf(temp_string,
sizeof(temp_string),
"%coutput=%s", separator, mode_chr);
@ -6266,6 +6316,8 @@ bool command_event(enum event_command cmd, void *data)
}
else
{
if (is_accessibility_enabled())
accessibility_speak((char*) msg_hash_to_str(MSG_UNPAUSED));
command_event(CMD_EVENT_UNPAUSE, NULL);
}
}
@ -6952,6 +7004,14 @@ TODO: Add a setting for these tweaks */
case CMD_EVENT_PAUSE_TOGGLE:
boolean = runloop_paused;
boolean = !boolean;
if (is_accessibility_enabled())
{
if (boolean)
accessibility_speak((char*) msg_hash_to_str(MSG_PAUSED));
else
accessibility_speak((char*) msg_hash_to_str(MSG_UNPAUSED));
}
runloop_paused = boolean;
retroarch_pause_checks();
break;
@ -7404,11 +7464,28 @@ TODO: Add a setting for these tweaks */
break;
case CMD_EVENT_AI_SERVICE_CALL:
{
#ifdef HAVE_TRANSLATE
settings_t *settings = configuration_settings;
if (settings->uints.ai_service_mode == 1 && is_ai_service_speech_running())
{
ai_service_speech_stop();
if (is_accessibility_enabled())
accessibility_speak("stopped.");
}
else if (is_accessibility_enabled() && settings->uints.ai_service_mode == 2 &&
is_narrator_running() == true)
{
accessibility_speak("stopped.");
}
else
{
RARCH_LOG("AI Service Called...\n");
run_translation_service();
}
#endif
break;
}
case CMD_EVENT_NONE:
return false;
}
@ -16365,6 +16442,90 @@ void input_keyboard_event(bool down, unsigned code,
uint32_t character, uint16_t mod, unsigned device)
{
static bool deferred_wait_keys;
if (menu_input_dialog_keyboard_display && down && is_accessibility_enabled())
{
if (code != 303 && code != 0)
{
char* say_char = malloc(sizeof(char));
if (say_char != NULL)
{
char c = (char) character;
*say_char = c;
if (character == 127)
accessibility_speak("backspace");
else if (c == '`')
accessibility_speak("left quote");
else if (c == '`')
accessibility_speak("tilde");
else if (c == '!')
accessibility_speak("exclamation point");
else if (c == '@')
accessibility_speak("at sign");
else if (c == '#')
accessibility_speak("hash sign");
else if (c == '$')
accessibility_speak("dollar sign");
else if (c == '%')
accessibility_speak("percent sign");
else if (c == '^')
accessibility_speak("carrot");
else if (c == '&')
accessibility_speak("ampersand");
else if (c == '*')
accessibility_speak("asterisk");
else if (c == '(')
accessibility_speak("left bracket");
else if (c == ')')
accessibility_speak("right bracket");
else if (c == '-')
accessibility_speak("minus");
else if (c == '_')
accessibility_speak("underscore");
else if (c == '=')
accessibility_speak("equals");
else if (c == '+')
accessibility_speak("plus");
else if (c == '[')
accessibility_speak("left square bracket");
else if (c == '{')
accessibility_speak("left curl bracket");
else if (c == ']')
accessibility_speak("right square bracket");
else if (c == '}')
accessibility_speak("right curl bracket");
else if (c == '\\')
accessibility_speak("back slash");
else if (c == '|')
accessibility_speak("pipe");
else if (c == ';')
accessibility_speak("semicolon");
else if (c == ':')
accessibility_speak("colon");
else if (c == '\'')
accessibility_speak("single quote");
else if (c == '\"')
accessibility_speak("double quote");
else if (c == ',')
accessibility_speak("comma");
else if (c == '<')
accessibility_speak("left angle bracket");
else if (c == '.')
accessibility_speak("period");
else if (c == '>')
accessibility_speak("right angle bracket");
else if (c == '/')
accessibility_speak("front slash");
else if (c == '?')
accessibility_speak("question mark");
else if (c == ' ')
accessibility_speak("space");
else if (character != 0)
accessibility_speak(say_char);
free(say_char);
}
}
}
if (deferred_wait_keys)
{
@ -23850,7 +24011,7 @@ static void retroarch_print_help(const char *arg0)
printf("Usage: %s [OPTIONS]... [FILE]\n", arg0);
{
char buf[2048];
char buf[2148];
buf[0] = '\0';
strlcpy(buf, " -h, --help Show this help message.\n", sizeof(buf));
@ -23927,7 +24088,7 @@ static void retroarch_print_help(const char *arg0)
"the device (1 to %d).\n", MAX_USERS);
{
char buf[2048];
char buf[2148];
buf[0] = '\0';
strlcpy(buf, " Format is PORT:ID, where ID is a number "
"corresponding to the particular device.\n", sizeof(buf));
@ -23983,6 +24144,8 @@ static void retroarch_print_help(const char *arg0)
" Path to save the screenshot to at the end of max-frames.\n", sizeof(buf));
puts(buf);
}
printf(" --accessibility\n"
" Enables accessibilty for blind users using text-to-speech.\n");
}
#define FFMPEG_RECORD_ARG "r:"
@ -24064,6 +24227,7 @@ static void retroarch_parse_input_and_config(int argc, char *argv[])
{ "eof-exit", 0, NULL, RA_OPT_EOF_EXIT },
{ "version", 0, NULL, RA_OPT_VERSION },
{ "log-file", 1, NULL, RA_OPT_LOG_FILE },
{ "accessibility", 0, NULL, RA_OPT_ACCESSIBILITY},
{ NULL, 0, NULL, 0 }
};
@ -24578,7 +24742,9 @@ static void retroarch_parse_input_and_config(int argc, char *argv[])
case '?':
retroarch_print_help(argv[0]);
retroarch_fail(1, "retroarch_parse_input()");
case RA_OPT_ACCESSIBILITY:
accessibility_enabled = true;
break;
default:
RARCH_ERR("%s\n", msg_hash_to_str(MSG_ERROR_PARSING_ARGUMENTS));
retroarch_fail(1, "retroarch_parse_input()");
@ -24777,6 +24943,9 @@ bool retroarch_main_init(int argc, char *argv[])
retroarch_parse_input_and_config(argc, argv);
if (is_accessibility_enabled())
accessibility_startup_message();
if (verbosity_is_enabled())
{
{
@ -26083,7 +26252,8 @@ void runloop_msg_queue_push(const char *msg,
enum message_queue_category category)
{
runloop_msg_queue_lock();
if (is_accessibility_enabled())
accessibility_speak_priority((char*) msg, 0);
#if defined(HAVE_MENU) && defined(HAVE_MENU_WIDGETS)
if (menu_widgets_inited)
{
@ -26608,7 +26778,6 @@ static enum runloop_state runloop_check_state(void)
bits_clear_bits(trigger_input.data, old_input.data,
ARRAY_SIZE(trigger_input.data));
action = (enum menu_action)menu_event(&current_bits, &trigger_input, display_kb);
focused = pause_nonactive ? is_focused : true;
focused = focused && !main_ui_companion_is_on_foreground;
@ -28778,3 +28947,422 @@ unsigned int retroarch_get_rotation(void)
settings_t *settings = configuration_settings;
return settings->uints.video_rotation + runloop_system.rotation;
}
/* Accessibility */
int speak_pid = 0;
bool is_accessibility_enabled()
{
return accessibility_enabled;
}
bool is_input_keyboard_display_on()
{
return menu_input_dialog_keyboard_display;
}
bool accessibility_speak(char* speak_text)
{
return accessibility_speak_priority(speak_text, 10);
}
bool accessibility_speak_priority(char* speak_text, int priority)
{
RARCH_LOG("Spoke: %s\n", speak_text);
const char* voice = NULL;
if (accessibility_enabled)
{
#if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)
voice = get_user_language_iso639_1(true);
return accessibility_speak_windows(speak_text, voice, priority);
#elif defined(__APPLE__) && defined(__MACH__)
voice = get_user_language_iso639_1(false);
return accessibility_speak_macos(speak_text, voice, priority);
#elif defined(__linux__) || defined(__unix__)
voice = get_user_language_iso639_1(true);
return accessibility_speak_linux(speak_text, voice, priority);
#endif
if (1==0)
{
return accessibility_speak_ai_service(speak_text, voice, priority);
}
}
return true;
}
bool is_narrator_running()
{
if (accessibility_enabled)
{
#if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)
return is_narrator_running_windows();
#elif defined(__APPLE__) && defined(__MACH__)
return is_narrator_running_macos();
#elif defined(__linux__) || defined(__unix__)
return is_narrator_running_linux();
#endif
}
return true;
}
#if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)
PROCESS_INFORMATION pi;
bool pi_set = false;
bool terminate_win32_process(PROCESS_INFORMATION pi)
{
TerminateProcess(pi.hProcess,0);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
return true;
}
bool CreateWin32Process(char* cmd)
{
STARTUPINFO si;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
if (!CreateProcess(NULL, cmd, NULL, NULL, FALSE, CREATE_NO_WINDOW,
NULL, NULL, &si, &pi))
{
pi_set = false;
return false;
}
pi_set = true;
return true;
}
char* accessibility_win_language_code(const char* language)
{
if (strcmp(language,"en") == 0)
return "Microsoft David Desktop";
else if (strcmp(language,"it") == 0)
return "Microsoft Cosimo Desktop";
else if (strcmp(language,"sv") == 0)
return "Microsoft Bengt Desktop";
else if (strcmp(language,"fr") == 0)
return "Microsoft Paul Desktop";
else if (strcmp(language,"de") == 0)
return "Microsoft Stefan Desktop";
else if (strcmp(language,"he") == 0)
return "Microsoft Hemant Desktop";
else if (strcmp(language,"id") == 0)
return "Microsoft Asaf Desktop";
else if (strcmp(language,"es") == 0)
return "Microsoft Pablo Desktop";
else if (strcmp(language,"nl") == 0)
return "Microsoft Frank Desktop";
else if (strcmp(language,"ro") == 0)
return "Microsoft Andrei Desktop";
else if (strcmp(language,"pt_pt") == 0)
return "Microsoft Helia Desktop";
else if (strcmp(language,"pt_bt") == 0 || strcmp(language,"pt") == 0)
return "Microsoft Daniel Desktop";
else if (strcmp(language,"th") == 0)
return "Microsoft Pattara Desktop";
else if (strcmp(language,"ja") == 0)
return "Microsoft Ichiro Desktop";
else if (strcmp(language,"sk") == 0)
return "Microsoft Filip Desktop";
else if (strcmp(language,"hi") == 0)
return "Microsoft Hemant Desktop";
else if (strcmp(language,"ar") == 0)
return "Microsoft Naayf Desktop";
else if (strcmp(language,"hu") == 0)
return "Microsoft Szabolcs Desktop";
else if (strcmp(language,"zh_tw") == 0 || strcmp(language,"zh")==0)
return "Microsoft Zhiwei Desktop";
else if (strcmp(language,"el") == 0)
return "Microsoft Stefanos Desktop";
else if (strcmp(language,"ru") == 0)
return "Microsoft Pavel Desktop";
else if (strcmp(language,"nb") == 0)
return "Microsoft Jon Desktop";
else if (strcmp(language,"da") == 0)
return "Microsoft Helle Desktop";
else if (strcmp(language,"fi") == 0)
return "Microsoft Heidi Desktop";
else if (strcmp(language,"zh_hk") == 0)
return "Microsoft Danny Desktop";
else if (strcmp(language,"zh_cn") == 0)
return "Microsoft Kangkang Desktop";
else if (strcmp(language,"tr") == 0)
return "Microsoft Tolga Desktop";
else if (strcmp(language,"ko") == 0)
return "Microsoft Heami Desktop";
else if (strcmp(language,"pl") == 0)
return "Microsoft Adam Desktop";
else if (strcmp(language,"cs") == 0)
return "Microsoft Jakub Desktop";
else
return "";
}
bool is_narrator_running_windows()
{
DWORD status = 0;
bool res;
if (pi_set == false)
return false;
res = GetExitCodeProcess(&pi, &status);
if (res == true && status == STILL_ACTIVE)
return true;
return false;
}
bool accessibility_speak_windows(char* speak_text, const char* voice, int priority)
{
char cmd[1200];
char* language = accessibility_win_language_code(voice);
bool res;
if (priority < 10)
{
if (is_narrator_running_windows())
return true;
}
if (strlen(language) > 0)
snprintf(cmd, sizeof(cmd),
"powershell.exe -NoProfile -WindowStyle Hidden -Command \"Add-Type -AssemblyName System.Speech; $synth = New-Object System.Speech.Synthesis.SpeechSynthesizer; $synth.SelectVoice(\\\"%s\\\"); $synth.Speak(\\\"%s\\\");\"", language, speak_text);
else
snprintf(cmd, sizeof(cmd),
"powershell.exe -NoProfile -WindowStyle Hidden -Command \"Add-Type -AssemblyName System.Speech; $synth = New-Object System.Speech.Synthesis.SpeechSynthesizer; $synth.Speak(\\\"%s\\\");\"", speak_text);
if (pi_set)
{
terminate_win32_process(pi);
}
res = CreateWin32Process(cmd);
if (!res)
{
RARCH_LOG("Create subprocess failed. Error: %d\n", GetLastError());
}
return true;
}
#endif
#if defined(__APPLE__) && defined(__MACH__)
char* accessibility_mac_language_code(const char* language)
{
if (strcmp(language,"en") == 0)
return "Alex";
else if (strcmp(language,"it") == 0)
return "Alice";
else if (strcmp(language,"sv") == 0)
return "Alva";
else if (strcmp(language,"fr") == 0)
return "Amelie";
else if (strcmp(language,"de") == 0)
return "Anna";
else if (strcmp(language,"he") == 0)
return "Carmit";
else if (strcmp(language,"id") == 0)
return "Damayanti";
else if (strcmp(language,"es") == 0)
return "Diego";
else if (strcmp(language,"nl") == 0)
return "Ellen";
else if (strcmp(language,"ro") == 0)
return "Ioana";
else if (strcmp(language,"pt_pt") == 0)
return "Joana";
else if (strcmp(language,"pt_bt") == 0 || strcmp(language,"pt") == 0)
return "Luciana";
else if (strcmp(language,"th") == 0)
return "Kanya";
else if (strcmp(language,"ja") == 0)
return "Kyoko";
else if (strcmp(language,"sk") == 0)
return "Laura";
else if (strcmp(language,"hi") == 0)
return "Lekha";
else if (strcmp(language,"ar") == 0)
return "Maged";
else if (strcmp(language,"hu") == 0)
return "Mariska";
else if (strcmp(language,"zh_tw") == 0 || strcmp(language,"zh")==0)
return "Mei-Jia";
else if (strcmp(language,"el") == 0)
return "Melina";
else if (strcmp(language,"ru") == 0)
return "Milena";
else if (strcmp(language,"nb") == 0)
return "Nora";
else if (strcmp(language,"da") == 0)
return "Sara";
else if (strcmp(language,"fi") == 0)
return "Satu";
else if (strcmp(language,"zh_hk") == 0)
return "Sin-ji";
else if (strcmp(language,"zh_cn") == 0)
return "Ting-Ting";
else if (strcmp(language,"tr") == 0)
return "Yelda";
else if (strcmp(language,"ko") == 0)
return "Yuna";
else if (strcmp(language,"pl") == 0)
return "Zosia";
else if (strcmp(language,"cs") == 0)
return "Zuzana";
else
return "";
}
bool is_narrator_running_macos()
{
if (kill(speak_pid, 0) == 0)
return true;
return false;
}
bool accessibility_speak_macos(char* speak_text, const char* voice, int priority)
{
int pid;
char* language_speaker = accessibility_mac_language_code(voice);
if (priority < 10 && speak_pid > 0)
{
/* check if old pid is running */
if (is_narrator_running_macos())
return true;
}
if (speak_pid > 0)
{
/* Kill the running espeak */
kill(speak_pid, SIGTERM);
speak_pid = 0;
}
pid = fork();
if (pid < 0)
{
/* error */
RARCH_LOG("ERROR: could not fork for say command.\n");
}
else if (pid > 0)
{
/* parent process */
speak_pid = pid;
/* Tell the system that we'll ignore the exit status of the child
* process. This prevents zombie processes. */
signal(SIGCHLD,SIG_IGN);
}
else
{
/* child process: replace process with the espeak command */
if (strlen(language_speaker)> 0)
execvp("say", (char* []) {"say", "-v", language_speaker,
speak_text, NULL});
else
execvp("say", (char* []) {"say", speak_text, NULL});
}
return true;
}
#endif
#if defined(__linux__) || defined(__unix__)
bool is_narrator_running_linux()
{
if (kill(speak_pid, 0) == 0)
return true;
return false;
}
bool accessibility_speak_linux(char* speak_text, const char* language, int priority)
{
int pid;
char* voice_out = malloc(3+strlen(language));
strcpy(voice_out, "-v");
strcat(voice_out, language);
if (priority < 10 && speak_pid > 0)
{
/* check if old pid is running */
if (is_narrator_running_linux())
return true;
}
if (speak_pid > 0)
{
/* Kill the running espeak */
kill(speak_pid, SIGTERM);
speak_pid = 0;
}
pid = fork();
if (pid < 0)
{
/* error */
RARCH_LOG("ERROR: could not fork for espeak.\n");
}
else if (pid > 0)
{
/* parent process */
speak_pid = pid;
/* Tell the system that we'll ignore the exit status of the child
* process. This prevents zombie processes. */
signal(SIGCHLD,SIG_IGN);
}
else
{
/* child process: replace process with the espeak command */
execvp("espeak", (char* []) {"espeak", voice_out, speak_text, NULL});
}
return true;
}
#endif
bool accessibility_speak_ai_service(char* speak_text, const char* language, int priority)
{
/* Call the ai service listed to do espeak for us. */
/* NOTE: This call works, but the audio mixer will not
* play sound files while the core is paused, so it's
* not practical at the moment. */
char new_ai_service_url[PATH_MAX_LENGTH];
char temp_string[PATH_MAX_LENGTH];
char json_buffer[2048];
char separator = '?';
settings_t *settings = configuration_settings;
strlcpy(new_ai_service_url, settings->arrays.ai_service_url,
sizeof(new_ai_service_url));
if (strrchr(new_ai_service_url, '?') != NULL)
separator = '&';
snprintf(temp_string, sizeof(temp_string),
"%csource_lang=%s&target_lang=%s&output=espeak",
separator, language, language);
strlcat(new_ai_service_url, temp_string, sizeof(new_ai_service_url));
strlcpy(temp_string, speak_text, sizeof(temp_string));
for (int i=0;i<strlen(temp_string);i++)
{
if (temp_string[i]=='\"')
temp_string[i] = ' ';
}
snprintf(json_buffer, sizeof(json_buffer),
"{\"text\": \"%s\"}", speak_text);
RARCH_LOG("SENDING accessibilty request... %s\n", new_ai_service_url);
task_push_http_post_transfer(new_ai_service_url,
json_buffer, true, NULL, handle_translation_cb, NULL);
return true;
}
bool accessibility_startup_message()
{
/* Note: for the ai service tts call, this is called too early... */
accessibility_speak("RetroArch accessibility on.");
return true;
}

View File

@ -2022,4 +2022,25 @@ unsigned int retroarch_get_rotation(void);
RETRO_END_DECLS
bool is_input_keyboard_display_on();
bool is_accessibility_enabled();
bool accessibility_speak(char* speak_text);
bool accessibility_speak_priority(char* speak_text, int priority);
bool accessibility_startup_message();
bool is_narrator_running();
#if defined(_WIN32) && !defined(_XBOX) && !defined(__WINRT__)
bool is_narrator_running_windows();
bool accessibility_speak_windows(char* speak_text, const char* voice, int priority);
#elif defined(__APPLE__) && defined(__MACH__)
bool is_narrator_running_macos();
char* accessibility_mac_language_code(const char* language);
bool accessibility_speak_macos(char* speak_text, const char* voice, int priority);
#elif defined(__linux__) || defined(__unix__)
bool is_narrator_running_linux();
bool accessibility_speak_linux(char* speak_text, const char* voice, int priority);
#endif
bool accessibility_speak_ai_service(char* speak_text, const char* voice, int priority);
#endif