Fix narrator modes (#16179)

* Fix narrator

* Refactor accessibility
This commit is contained in:
Viačasłaŭ Chalikin 2024-02-09 05:14:23 +00:00 committed by GitHub
parent 156dd055d9
commit 997c7453a8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 358 additions and 359 deletions

View File

@ -41,10 +41,10 @@ typedef struct
{ {
/* The last request task, used to prepare and send the translation */ /* The last request task, used to prepare and send the translation */
retro_task_t *request_task; retro_task_t *request_task;
/* The last response task, used to parse costly translation data */ /* The last response task, used to parse costly translation data */
retro_task_t *response_task; retro_task_t *response_task;
/* Timestamp of the last translation request */ /* Timestamp of the last translation request */
retro_time_t last_call; retro_time_t last_call;
@ -59,7 +59,7 @@ typedef struct
/* 1 if the automatic mode has been enabled, 0 otherwise */ /* 1 if the automatic mode has been enabled, 0 otherwise */
int ai_service_auto; int ai_service_auto;
/* Text-to-speech narrator override flag */ /* Text-to-speech narrator override flag */
bool enabled; bool enabled;
} access_state_t; } access_state_t;
@ -71,26 +71,26 @@ bool is_narrator_running(bool accessibility_enable);
#endif #endif
/* /*
Invoke this method to send a request to the AI service. Invoke this method to send a request to the AI service.
It makes the following POST request using URL params: It makes the following POST request using URL params:
source_lang (optional): language code of the content currently running. source_lang (optional): language code of the content currently running.
target_lang (optional): language of the content to return. target_lang (optional): language of the content to return.
output: comma-separated list of formats that must be provided by the output: comma-separated list of formats that must be provided by the
service. Also lists supported sub-formats. service. Also lists supported sub-formats.
The currently supported formats are: The currently supported formats are:
sound: raw audio to playback. (wav) sound: raw audio to playback. (wav)
text: text to be read through internal text-to-speech capabilities. text: text to be read through internal text-to-speech capabilities.
'subs' can be specified on top of that to explain that we are looking 'subs' can be specified on top of that to explain that we are looking
for short text response in the manner of subtitles. for short text response in the manner of subtitles.
image: image to display on top of the video feed. Widgets will be used image: image to display on top of the video feed. Widgets will be used
first if possible, otherwise we'll try to draw it directly on the first if possible, otherwise we'll try to draw it directly on the
video buffer. (bmp, png, png-a) [All in 24-bits BGR formats] video buffer. (bmp, png, png-a) [All in 24-bits BGR formats]
In addition, the request contains a JSON payload, formatted as such: In addition, the request contains a JSON payload, formatted as such:
image: captured frame from the currently running content (in base64). image: captured frame from the currently running content (in base64).
format: format of the captured frame ("png", or "bmp"). format: format of the captured frame ("png", or "bmp").
coords: array describing the coordinates of the image within the coords: array describing the coordinates of the image within the
viewport space (x, y, width, height). viewport space (x, y, width, height).
viewport: array describing the size of the viewport (width, height). viewport: array describing the size of the viewport (width, height).
label: a text string describing the content (<system id>__<content id>). label: a text string describing the content (<system id>__<content id>).
@ -99,7 +99,7 @@ bool is_narrator_running(bool accessibility_enable);
<key>: the name of a retropad input, valued 1 if pressed. <key>: the name of a retropad input, valued 1 if pressed.
(a, b, x, y, l, r, l2, r2, l3, r3) (a, b, x, y, l, r, l2, r2, l3, r3)
(up, down, left, right, start, select) (up, down, left, right, start, select)
The translation component then expects a response from the AI service in the The translation component then expects a response from the AI service in the
form of a JSON payload, formatted as such: form of a JSON payload, formatted as such:
image: base64 representation of an image in a supported format. image: base64 representation of an image in a supported format.
@ -108,40 +108,49 @@ bool is_narrator_running(bool accessibility_enable);
text_position: hint for the position of the text when the service is text_position: hint for the position of the text when the service is
running in text mode (ie subtitles). Position is a number, running in text mode (ie subtitles). Position is a number,
1 for Bottom or 2 for Top (defaults to bottom). 1 for Bottom or 2 for Top (defaults to bottom).
press: a list of retropad input to forcibly press. On top of the press: a list of retropad input to forcibly press. On top of the
expected keys (cf. 'state' above) values 'pause' and 'unpause' can be expected keys (cf. 'state' above) values 'pause' and 'unpause' can be
specified to control the flow of the content. specified to control the flow of the content.
error: any error encountered with the request. error: any error encountered with the request.
auto: either 'auto' or 'continue' to control automatic requests. auto: either 'auto' or 'continue' to control automatic requests.
All fields are optional, but at least one of them must be present. All fields are optional, but at least one of them must be present.
If 'error' is set, the error is shown to the user and everything else is If 'error' is set, the error is shown to the user and everything else is
ignored, even 'auto' settings. ignored, even 'auto' settings.
With 'auto' on 'auto', RetroArch will automatically send a new request With 'auto' on 'auto', RetroArch will automatically send a new request
(with a minimum delay enforced by uints.ai_service_poll_delay), with a value (with a minimum delay enforced by uints.ai_service_poll_delay), with a value
of 'continue', RetroArch will ignore the returned content and skip to the of 'continue', RetroArch will ignore the returned content and skip to the
next automatic request. This allows the service to specify that the returned next automatic request. This allows the service to specify that the returned
content is the same as the one previously sent, so RetroArch does not need to content is the same as the one previously sent, so RetroArch does not need to
update its display unless necessary. With 'continue' the service *must* update its display unless necessary. With 'continue' the service *must*
still send the content, as we may need to display it if the user paused the still send the content, as we may need to display it if the user paused the
AI service for instance. AI service for instance.
{paused} boolean is passed in to indicate if the current call was made {paused} boolean is passed in to indicate if the current call was made
during a paused frame. Due to how the menu widgets work, if the AI service during a paused frame. Due to how the menu widgets work, if the AI service
is called in 'auto' mode, then this call will be made while the menu widgets is called in 'auto' mode, then this call will be made while the menu widgets
unpause the core for a frame to update the on-screen widgets. To tell the AI unpause the core for a frame to update the on-screen widgets. To tell the AI
service what the pause mode is honestly, we store the runloop_paused service what the pause mode is honestly, we store the runloop_paused
variable from before the service wipes the widgets, and pass that in here. variable from before the service wipes the widgets, and pass that in here.
*/ */
bool run_translation_service(settings_t *settings, bool paused); bool run_translation_service(settings_t *settings, bool paused);
void translation_release(bool inform); void translation_release(bool inform);
bool accessibility_speak_priority( /* Proxy for calls related to menu navigation */
bool navigation_say(
bool accessibility_enable, bool accessibility_enable,
unsigned accessibility_narrator_speech_speed, unsigned accessibility_narrator_speech_speed,
const char* speak_text, int priority); const char* speak_text,
int priority);
/* Local platform-specific TTS */
bool accessibility_speak_priority(
unsigned accessibility_narrator_speech_speed,
const char *speak_text,
int priority,
const char* voice);
access_state_t *access_state_get_ptr(void); access_state_t *access_state_get_ptr(void);

View File

@ -885,10 +885,9 @@ static bool is_narrator_running_macos(void)
} }
static bool accessibility_speak_macos(int speed, static bool accessibility_speak_macos(int speed,
const char* speak_text, int priority) const char* speak_text, int priority, const char* voice)
{ {
int pid; int pid;
const char *voice = get_user_language_iso639_1(false);
char* language_speaker = accessibility_mac_language_code(voice); char* language_speaker = accessibility_mac_language_code(voice);
char* speeds[10] = {"80", "100", "125", "150", "170", "210", char* speeds[10] = {"80", "100", "125", "150", "170", "210",
"260", "310", "380", "450"}; "260", "310", "380", "450"};

View File

@ -82,7 +82,6 @@
#include "../../msg_hash.h" #include "../../msg_hash.h"
#include "../../paths.h" #include "../../paths.h"
#include "../../retroarch.h" #include "../../retroarch.h"
#include "../../translation_defines.h"
#include "../../verbosity.h" #include "../../verbosity.h"
#ifdef HAVE_MENU #ifdef HAVE_MENU
@ -798,7 +797,7 @@ static void check_proc_acpi_sysfs_battery(const char *node,
} }
fill_pathname_join_special(path, basenode, "type", sizeof(path)); fill_pathname_join_special(path, basenode, "type", sizeof(path));
if (!filestream_exists(path)) if (!filestream_exists(path))
goto status; goto status;
@ -813,7 +812,7 @@ static void check_proc_acpi_sysfs_battery(const char *node,
buf = NULL; buf = NULL;
} }
status: status:
fill_pathname_join_special(path, basenode, "status", sizeof(path)); fill_pathname_join_special(path, basenode, "status", sizeof(path));
if (!filestream_exists(path)) if (!filestream_exists(path))
@ -2789,152 +2788,106 @@ static bool is_narrator_running_unix(void)
return (kill(speak_pid, 0) == 0); return (kill(speak_pid, 0) == 0);
} }
/** static const char* accessibility_unix_language_code(const char* language)
* Returns the espeak-compatible string representation of the translation language enum value.
*/
static const char* espeak_get_str(enum translation_lang id)
{ {
switch (id) if (
{ string_is_equal(language, "en") ||
case TRANSLATION_LANG_EN: string_is_equal(language, "it") ||
return "en"; string_is_equal(language, "sv") ||
case TRANSLATION_LANG_ES: string_is_equal(language, "fr") ||
return "es"; string_is_equal(language, "de") ||
case TRANSLATION_LANG_FR: string_is_equal(language, "he") ||
return "fr"; string_is_equal(language, "id") ||
case TRANSLATION_LANG_IT: string_is_equal(language, "es") ||
return "it"; string_is_equal(language, "nl") ||
case TRANSLATION_LANG_DE: string_is_equal(language, "ro") ||
return "de"; string_is_equal(language, "th") ||
case TRANSLATION_LANG_JP: string_is_equal(language, "ja") ||
return "ja"; string_is_equal(language, "sk") ||
case TRANSLATION_LANG_NL: string_is_equal(language, "hi") ||
return "nl"; string_is_equal(language, "ar") ||
case TRANSLATION_LANG_CS: string_is_equal(language, "hu") ||
return "cs"; string_is_equal(language, "el") ||
case TRANSLATION_LANG_DA: string_is_equal(language, "ru") ||
return "da"; string_is_equal(language, "nb") ||
case TRANSLATION_LANG_SV: string_is_equal(language, "da") ||
return "sv"; string_is_equal(language, "fi") ||
case TRANSLATION_LANG_HR: string_is_equal(language, "tr") ||
return "hr"; string_is_equal(language, "ko") ||
case TRANSLATION_LANG_KO: string_is_equal(language, "pl") ||
return "ko"; string_is_equal(language, "cs") ||
case TRANSLATION_LANG_ZH_CN: string_is_equal(language, "eo") ||
case TRANSLATION_LANG_ZH_TW: string_is_equal(language, "vi") ||
return "cmn"; string_is_equal(language, "fa") ||
case TRANSLATION_LANG_CA: string_is_equal(language, "uk") ||
return "ca"; string_is_equal(language, "be") ||
case TRANSLATION_LANG_BG: string_is_equal(language, "hr") ||
return "bg"; string_is_equal(language, "bg") ||
case TRANSLATION_LANG_BN: string_is_equal(language, "bn") ||
return "bn"; string_is_equal(language, "eu") ||
case TRANSLATION_LANG_EU: string_is_equal(language, "az") ||
return "eu"; string_is_equal(language, "sq") ||
case TRANSLATION_LANG_AZ: string_is_equal(language, "af") ||
return "az"; string_is_equal(language, "et") ||
case TRANSLATION_LANG_AR: string_is_equal(language, "ka") ||
return "ar"; string_is_equal(language, "gu") ||
case TRANSLATION_LANG_SQ: string_is_equal(language, "ht") ||
return "sq"; string_is_equal(language, "is") ||
case TRANSLATION_LANG_AF: string_is_equal(language, "ga") ||
return "af"; string_is_equal(language, "kn") ||
case TRANSLATION_LANG_EO: string_is_equal(language, "la") ||
return "eo"; string_is_equal(language, "lv") ||
case TRANSLATION_LANG_ET: string_is_equal(language, "lt") ||
return "et"; string_is_equal(language, "mk") ||
case TRANSLATION_LANG_FI: string_is_equal(language, "ms") ||
return "fi"; string_is_equal(language, "mt") ||
case TRANSLATION_LANG_KA: string_is_equal(language, "sr") ||
return "ka"; string_is_equal(language, "sl") ||
case TRANSLATION_LANG_EL: string_is_equal(language, "sw") ||
return "el"; string_is_equal(language, "ta") ||
case TRANSLATION_LANG_GU: string_is_equal(language, "te") ||
return "gu"; string_is_equal(language, "ur") ||
case TRANSLATION_LANG_HT: string_is_equal(language, "cy")
return "ht"; )
case TRANSLATION_LANG_HE: return language;
return "he"; else if (
case TRANSLATION_LANG_HI: string_is_equal(language, "no") ||
return "hi"; string_is_equal(language, "nb")
case TRANSLATION_LANG_HU: )
return "hu"; return "nb";
case TRANSLATION_LANG_IS: else if (string_is_equal(language, "en_gb"))
return "is"; return "en-gb";
case TRANSLATION_LANG_ID: else if (
return "id"; string_is_equal(language, "ca") ||
case TRANSLATION_LANG_GA: string_is_equal(language, "ca_ES@valencia")
return "ga"; )
case TRANSLATION_LANG_KN: return "ca";
return "kn"; else if (
case TRANSLATION_LANG_LA: string_is_equal(language, "pt_pt") ||
return "la"; string_is_equal(language, "pt")
case TRANSLATION_LANG_LV: )
return "lv"; return "pt";
case TRANSLATION_LANG_LT: else if (string_is_equal(language, "pt_bt"))
return "lt"; return "pt-br";
case TRANSLATION_LANG_MK: else if (
return "mk"; string_is_equal(language, "zh") ||
case TRANSLATION_LANG_MS: string_is_equal(language, "zh_cn") ||
return "ms"; string_is_equal(language, "zh_tw") ||
case TRANSLATION_LANG_MT: string_is_equal(language, "zh-CN") ||
return "mt"; string_is_equal(language, "zh-TW")
case TRANSLATION_LANG_NO: )
return "nb"; return "cmn";
case TRANSLATION_LANG_FA: else if (string_is_equal(language, "zh_hk"))
return "fa"; return "yue";
case TRANSLATION_LANG_PL: /* default voice as fallback */
return "pl";
case TRANSLATION_LANG_PT:
return "pt";
case TRANSLATION_LANG_RO:
return "ro";
case TRANSLATION_LANG_RU:
return "ru";
case TRANSLATION_LANG_SR:
return "sr";
case TRANSLATION_LANG_SK:
return "sk";
case TRANSLATION_LANG_SL:
return "sl";
case TRANSLATION_LANG_SW:
return "sw";
case TRANSLATION_LANG_TA:
return "ta";
case TRANSLATION_LANG_TE:
return "te";
case TRANSLATION_LANG_TH:
return "th";
case TRANSLATION_LANG_TR:
return "tr";
case TRANSLATION_LANG_UK:
return "uk";
case TRANSLATION_LANG_BE:
return "be";
case TRANSLATION_LANG_UR:
return "ur";
case TRANSLATION_LANG_VI:
return "vi";
case TRANSLATION_LANG_CY:
return "cy";
case TRANSLATION_LANG_AST:
case TRANSLATION_LANG_TL:
case TRANSLATION_LANG_GL:
case TRANSLATION_LANG_YI:
case TRANSLATION_LANG_DONT_CARE:
case TRANSLATION_LANG_LAST:
break;
}
return "en"; return "en";
} }
static bool accessibility_speak_unix(int speed, static bool accessibility_speak_unix(int speed,
const char* speak_text, int priority) const char* speak_text, int priority, const char* voice)
{ {
int pid; int pid;
settings_t *settings = config_get_ptr(); const char* language = accessibility_unix_language_code(voice);
unsigned target_lang = settings->uints.ai_service_target_lang;
const char *language = espeak_get_str((enum translation_lang)target_lang);
char* voice_out = (char*)malloc(3 + strlen(language)); char* voice_out = (char*)malloc(3 + strlen(language));
char* speed_out = (char*)malloc(3 + 3); char* speed_out = (char*)malloc(3 + 3);
const char* speeds[10] = {"80", "100", "125", "150", "170", "210", "260", "310", "380", "450"}; const char* speeds[10] = {"80", "100", "125", "150", "170", "210", "260", "310", "380", "450"};
@ -2994,7 +2947,7 @@ static bool accessibility_speak_unix(int speed,
/* Tell the system that we'll ignore the exit status of the child /* Tell the system that we'll ignore the exit status of the child
* process. This prevents zombie processes. */ * process. This prevents zombie processes. */
signal(SIGCHLD, SIG_IGN); signal(SIGCHLD, SIG_IGN);
} }
} }
end: end:

View File

@ -831,7 +831,7 @@ static const char *accessibility_win_language_id(const char* language)
return "401"; return "401";
else if (string_is_equal(language,"hu")) else if (string_is_equal(language,"hu"))
return "040e"; return "040e";
else if (string_is_equal(language,"zh_tw") || string_is_equal(language,"zh")) else if (string_is_equal(language, "zh_tw") || string_is_equal(language,"zh"))
return "804"; return "804";
else if (string_is_equal(language,"el")) else if (string_is_equal(language,"el"))
return "408"; return "408";
@ -896,13 +896,15 @@ static const char *accessibility_win_language_code(const char* language)
return "Microsoft Naayf Desktop"; return "Microsoft Naayf Desktop";
else if (string_is_equal(language,"hu")) else if (string_is_equal(language,"hu"))
return "Microsoft Szabolcs Desktop"; return "Microsoft Szabolcs Desktop";
else if (string_is_equal(language,"zh_tw") || string_is_equal(language,"zh")) else if (string_is_equal(language, "zh_tw")
|| string_is_equal(language,"zh-TW")
|| string_is_equal(language,"zh"))
return "Microsoft Zhiwei Desktop"; return "Microsoft Zhiwei Desktop";
else if (string_is_equal(language,"el")) else if (string_is_equal(language,"el"))
return "Microsoft Stefanos Desktop"; return "Microsoft Stefanos Desktop";
else if (string_is_equal(language,"ru")) else if (string_is_equal(language,"ru"))
return "Microsoft Pavel Desktop"; return "Microsoft Pavel Desktop";
else if (string_is_equal(language,"nb")) else if (string_is_equal(language,"no") || string_is_equal(language,"nb"))
return "Microsoft Jon Desktop"; return "Microsoft Jon Desktop";
else if (string_is_equal(language,"da")) else if (string_is_equal(language,"da"))
return "Microsoft Helle Desktop"; return "Microsoft Helle Desktop";
@ -910,7 +912,7 @@ static const char *accessibility_win_language_code(const char* language)
return "Microsoft Heidi Desktop"; return "Microsoft Heidi Desktop";
else if (string_is_equal(language,"zh_hk")) else if (string_is_equal(language,"zh_hk"))
return "Microsoft Danny Desktop"; return "Microsoft Danny Desktop";
else if (string_is_equal(language,"zh_cn")) else if (string_is_equal(language,"zh_cn") || string_is_equal(language,"zh-CN"))
return "Microsoft Kangkang Desktop"; return "Microsoft Kangkang Desktop";
else if (string_is_equal(language,"tr")) else if (string_is_equal(language,"tr"))
return "Microsoft Tolga Desktop"; return "Microsoft Tolga Desktop";
@ -918,8 +920,24 @@ static const char *accessibility_win_language_code(const char* language)
return "Microsoft Heami Desktop"; return "Microsoft Heami Desktop";
else if (string_is_equal(language,"pl")) else if (string_is_equal(language,"pl"))
return "Microsoft Adam Desktop"; return "Microsoft Adam Desktop";
else if (string_is_equal(language,"cs")) else if (string_is_equal(language,"cs"))
return "Microsoft Jakub Desktop"; return "Microsoft Jakub Desktop";
else if (string_is_equal(language,"vi"))
return "Microsoft An Desktop";
else if (string_is_equal(language,"hr"))
return "Microsoft Matej Desktop";
else if (string_is_equal(language,"bg"))
return "Microsoft Ivan Desktop";
else if (string_is_equal(language,"ms"))
return "Microsoft Rizwan Desktop";
else if (string_is_equal(language,"sl"))
return "Microsoft Lado Desktop";
else if (string_is_equal(language,"ta"))
return "Microsoft Valluvar Desktop";
else if (string_is_equal(language,"en_gb"))
return "Microsoft George Desktop";
else if (string_is_equal(language,"ca") || string_is_equal(language,"ca_ES@valencia"))
return "Microsoft Herena Desktop";
return ""; return "";
} }
@ -1017,10 +1035,9 @@ static bool is_narrator_running_windows(void)
} }
static bool accessibility_speak_windows(int speed, static bool accessibility_speak_windows(int speed,
const char* speak_text, int priority) const char* speak_text, int priority, const char* voice)
{ {
char cmd[512]; char cmd[512];
const char *voice = get_user_language_iso639_1(true);
const char *language = accessibility_win_language_code(voice); const char *language = accessibility_win_language_code(voice);
const char *langid = accessibility_win_language_id(voice); const char *langid = accessibility_win_language_id(voice);
bool res = false; bool res = false;
@ -1069,7 +1086,7 @@ static bool accessibility_speak_windows(int speed,
g_plat_win32_flags |= PLAT_WIN32_FLAG_USE_POWERSHELL; g_plat_win32_flags |= PLAT_WIN32_FLAG_USE_POWERSHELL;
if (wc) if (wc)
free(wc); free(wc);
return accessibility_speak_windows(speed, speak_text, priority); return accessibility_speak_windows(speed, speak_text, priority, voice);
} }
nvdaController_cancelSpeech_func(); nvdaController_cancelSpeech_func();

View File

@ -112,7 +112,7 @@ typedef struct frontend_ctx_driver
enum retro_language (*get_user_language)(void); enum retro_language (*get_user_language)(void);
bool (*is_narrator_running)(void); bool (*is_narrator_running)(void);
bool (*accessibility_speak)(int speed, bool (*accessibility_speak)(int speed,
const char* speak_text, int priority); const char* speak_text, int priority, const char* voice);
bool (*set_gamemode)(bool on); bool (*set_gamemode)(bool on);
const char *ident; const char *ident;

View File

@ -5083,7 +5083,7 @@ static void input_keys_pressed(
void bsv_movie_free(bsv_movie_t*); void bsv_movie_free(bsv_movie_t*);
void bsv_movie_enqueue(input_driver_state_t *input_st, bsv_movie_t * state, enum bsv_flags flags) void bsv_movie_enqueue(input_driver_state_t *input_st, bsv_movie_t * state, enum bsv_flags flags)
{ {
if (input_st->bsv_movie_state_next_handle) if (input_st->bsv_movie_state_next_handle)
bsv_movie_free(input_st->bsv_movie_state_next_handle); bsv_movie_free(input_st->bsv_movie_state_next_handle);
input_st->bsv_movie_state_next_handle = state; input_st->bsv_movie_state_next_handle = state;
@ -6614,7 +6614,7 @@ void input_keyboard_event(bool down, unsigned code,
say_char[1] = '\0'; say_char[1] = '\0';
if (character == 127 || character == 8) if (character == 127 || character == 8)
accessibility_speak_priority( navigation_say(
accessibility_enable, accessibility_enable,
accessibility_narrator_speech_speed, accessibility_narrator_speech_speed,
"backspace", 10); "backspace", 10);
@ -6622,12 +6622,12 @@ void input_keyboard_event(bool down, unsigned code,
{ {
const char *lut_name = accessibility_lut_name(c); const char *lut_name = accessibility_lut_name(c);
if (lut_name) if (lut_name)
accessibility_speak_priority( navigation_say(
accessibility_enable, accessibility_enable,
accessibility_narrator_speech_speed, accessibility_narrator_speech_speed,
lut_name, 10); lut_name, 10);
else if (character != 0) else if (character != 0)
accessibility_speak_priority( navigation_say(
accessibility_enable, accessibility_enable,
accessibility_narrator_speech_speed, accessibility_narrator_speech_speed,
say_char, 10); say_char, 10);

View File

@ -7016,7 +7016,7 @@ static int generic_menu_iterate(
&& is_accessibility_enabled( && is_accessibility_enabled(
accessibility_enable, accessibility_enable,
access_st->enabled)) access_st->enabled))
accessibility_speak_priority( navigation_say(
accessibility_enable, accessibility_enable,
accessibility_narrator_speech_speed, accessibility_narrator_speech_speed,
menu->menu_state_msg, 10); menu->menu_state_msg, 10);
@ -7148,18 +7148,18 @@ static int generic_menu_iterate(
menu_st, menu_st,
current_sublabel, sizeof(current_sublabel)); current_sublabel, sizeof(current_sublabel));
if (string_is_equal(current_sublabel, "")) if (string_is_equal(current_sublabel, ""))
accessibility_speak_priority( navigation_say(
accessibility_enable, accessibility_enable,
accessibility_narrator_speech_speed, accessibility_narrator_speech_speed,
menu->menu_state_msg, 10); menu->menu_state_msg, 10);
else else
accessibility_speak_priority( navigation_say(
accessibility_enable, accessibility_enable,
accessibility_narrator_speech_speed, accessibility_narrator_speech_speed,
current_sublabel, 10); current_sublabel, 10);
} }
else else
accessibility_speak_priority( navigation_say(
accessibility_enable, accessibility_enable,
accessibility_narrator_speech_speed, accessibility_narrator_speech_speed,
menu->menu_state_msg, 10); menu->menu_state_msg, 10);
@ -7321,7 +7321,7 @@ static int generic_menu_iterate(
&& is_accessibility_enabled( && is_accessibility_enabled(
accessibility_enable, accessibility_enable,
access_st->enabled)) access_st->enabled))
accessibility_speak_priority( navigation_say(
accessibility_enable, accessibility_enable,
accessibility_narrator_speech_speed, accessibility_narrator_speech_speed,
"Closed dialog.", 10); "Closed dialog.", 10);
@ -7759,7 +7759,7 @@ int generic_menu_entry_action(
} }
if (!string_is_empty(speak_string)) if (!string_is_empty(speak_string))
accessibility_speak_priority( navigation_say(
accessibility_enable, accessibility_enable,
accessibility_narrator_speech_speed, accessibility_narrator_speech_speed,
speak_string, 10); speak_string, 10);
@ -7892,7 +7892,7 @@ bool menu_input_dialog_start_search(void)
if (is_accessibility_enabled( if (is_accessibility_enabled(
accessibility_enable, accessibility_enable,
access_st->enabled)) access_st->enabled))
accessibility_speak_priority( navigation_say(
accessibility_enable, accessibility_enable,
accessibility_narrator_speech_speed, accessibility_narrator_speech_speed,
(char*)msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SEARCH), 10); (char*)msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SEARCH), 10);
@ -7946,7 +7946,7 @@ bool menu_input_dialog_start(menu_input_ctx_line_t *line)
if (is_accessibility_enabled( if (is_accessibility_enabled(
accessibility_enable, accessibility_enable,
access_st->enabled)) access_st->enabled))
accessibility_speak_priority( navigation_say(
accessibility_enable, accessibility_enable,
accessibility_narrator_speech_speed, accessibility_narrator_speech_speed,
"Keyboard input:", 10); "Keyboard input:", 10);

View File

@ -3169,7 +3169,7 @@ bool command_event(enum event_command cmd, void *data)
if (is_accessibility_enabled( if (is_accessibility_enabled(
accessibility_enable, accessibility_enable,
access_st->enabled)) access_st->enabled))
accessibility_speak_priority( navigation_say(
accessibility_enable, accessibility_enable,
accessibility_narrator_speech_speed, accessibility_narrator_speech_speed,
(char*)msg_hash_to_str(MSG_UNPAUSED), 10); (char*)msg_hash_to_str(MSG_UNPAUSED), 10);
@ -4559,12 +4559,12 @@ bool command_event(enum event_command cmd, void *data)
access_st->enabled)) access_st->enabled))
{ {
if (paused) if (paused)
accessibility_speak_priority( navigation_say(
accessibility_enable, accessibility_enable,
accessibility_narrator_speech_speed, accessibility_narrator_speech_speed,
(char*)msg_hash_to_str(MSG_PAUSED), 10); (char*)msg_hash_to_str(MSG_PAUSED), 10);
else else
accessibility_speak_priority( navigation_say(
accessibility_enable, accessibility_enable,
accessibility_narrator_speech_speed, accessibility_narrator_speech_speed,
(char*)msg_hash_to_str(MSG_UNPAUSED), 10); (char*)msg_hash_to_str(MSG_UNPAUSED), 10);
@ -5312,7 +5312,7 @@ bool command_event(enum event_command cmd, void *data)
if (is_accessibility_enabled( if (is_accessibility_enabled(
accessibility_enable, accessibility_enable,
access_st->enabled)) access_st->enabled))
accessibility_speak_priority( navigation_say(
accessibility_enable, accessibility_enable,
accessibility_narrator_speech_speed, accessibility_narrator_speech_speed,
(char*)msg_hash_to_str(MSG_AI_SERVICE_STOPPED), (char*)msg_hash_to_str(MSG_AI_SERVICE_STOPPED),
@ -5327,7 +5327,7 @@ bool command_event(enum event_command cmd, void *data)
access_st->enabled) access_st->enabled)
&& (ai_service_mode == 2) && (ai_service_mode == 2)
&& is_narrator_running(accessibility_enable)) && is_narrator_running(accessibility_enable))
accessibility_speak_priority( navigation_say(
accessibility_enable, accessibility_enable,
accessibility_narrator_speech_speed, accessibility_narrator_speech_speed,
(char*)msg_hash_to_str(MSG_AI_SERVICE_STOPPED), (char*)msg_hash_to_str(MSG_AI_SERVICE_STOPPED),
@ -7386,7 +7386,7 @@ bool retroarch_main_init(int argc, char *argv[])
if (is_accessibility_enabled( if (is_accessibility_enabled(
accessibility_enable, accessibility_enable,
access_st->enabled)) access_st->enabled))
accessibility_speak_priority( navigation_say(
accessibility_enable, accessibility_enable,
accessibility_narrator_speech_speed, accessibility_narrator_speech_speed,
(char*)msg_hash_to_str(MSG_ACCESSIBILITY_STARTUP), (char*)msg_hash_to_str(MSG_ACCESSIBILITY_STARTUP),
@ -8337,7 +8337,7 @@ void retroarch_favorites_deinit(void)
} }
#ifdef HAVE_ACCESSIBILITY #ifdef HAVE_ACCESSIBILITY
bool accessibility_speak_priority( bool navigation_say(
bool accessibility_enable, bool accessibility_enable,
unsigned accessibility_narrator_speech_speed, unsigned accessibility_narrator_speech_speed,
const char* speak_text, int priority) const char* speak_text, int priority)
@ -8347,29 +8347,48 @@ bool accessibility_speak_priority(
accessibility_enable, accessibility_enable,
access_st->enabled)) access_st->enabled))
{ {
frontend_ctx_driver_t *frontend = const char *voice = get_user_language_iso639_1(false);
frontend_state_get_ptr()->current_frontend_ctx; bool native_narrator = accessibility_speak_priority(accessibility_narrator_speech_speed,
speak_text, priority, voice);
RARCH_LOG("Spoke: %s\n", speak_text); if (!native_narrator)
{
if (frontend && frontend->accessibility_speak) /*
return frontend->accessibility_speak(accessibility_narrator_speech_speed, speak_text, * The following method is a fallback for other platforms to use the
priority); * AI Service url to do the TTS. However, since the playback is done
* via the audio mixer, which only processes the audio while the
RARCH_LOG("Platform not supported for accessibility.\n"); * core is running, this playback method won't work. When the audio
/* The following method is a fallback for other platforms to use the * mixer can handle playing streams while the core is paused, then
AI Service url to do the TTS. However, since the playback is done * we can use this.
via the audio mixer, which only processes the audio while the */
core is running, this playback method won't work. When the audio
mixer can handle playing streams while the core is paused, then
we can use this. */
#if 0 #if 0
#if defined(HAVE_NETWORKING) #if defined(HAVE_NETWORKING)
return accessibility_speak_ai_service(speak_text, voice, priority); return accessibility_speak_ai_service(speak_text, voice, priority);
#endif #endif
#endif #endif
}
} }
return true; return true;
} }
bool accessibility_speak_priority(
unsigned accessibility_narrator_speech_speed,
const char *speak_text,
int priority,
const char *voice)
{
frontend_ctx_driver_t *frontend =
frontend_state_get_ptr()->current_frontend_ctx;
RARCH_LOG("Spoke: %s\n", speak_text);
if (frontend && frontend->accessibility_speak)
return frontend->accessibility_speak(accessibility_narrator_speech_speed,
speak_text, priority, voice);
RARCH_LOG("Platform not supported for accessibility.\n");
return false;
}
#endif #endif

View File

@ -5311,7 +5311,7 @@ void runloop_msg_queue_push(const char *msg,
if (is_accessibility_enabled( if (is_accessibility_enabled(
accessibility_enable, accessibility_enable,
access_st->enabled)) access_st->enabled))
accessibility_speak_priority( navigation_say(
accessibility_enable, accessibility_enable,
accessibility_narrator_speech_speed, accessibility_narrator_speech_speed,
(char*) msg, 0); (char*) msg, 0);
@ -7336,7 +7336,7 @@ void runloop_task_msg_queue_push(
if (is_accessibility_enabled( if (is_accessibility_enabled(
accessibility_enable, accessibility_enable,
access_st->enabled)) access_st->enabled))
accessibility_speak_priority( navigation_say(
accessibility_enable, accessibility_enable,
accessibility_narrator_speech_speed, accessibility_narrator_speech_speed,
(char*)msg, 0); (char*)msg, 0);

View File

@ -110,6 +110,151 @@ typedef struct
/* UTILITIES ---------------------------------------------------------------- */ /* UTILITIES ---------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/**
* Returns the string representation of the translation language enum value.
*/
static const char* ai_service_get_str(enum translation_lang id)
{
switch (id)
{
case TRANSLATION_LANG_EN:
return "en";
case TRANSLATION_LANG_ES:
return "es";
case TRANSLATION_LANG_FR:
return "fr";
case TRANSLATION_LANG_IT:
return "it";
case TRANSLATION_LANG_DE:
return "de";
case TRANSLATION_LANG_JP:
return "ja";
case TRANSLATION_LANG_NL:
return "nl";
case TRANSLATION_LANG_CS:
return "cs";
case TRANSLATION_LANG_DA:
return "da";
case TRANSLATION_LANG_SV:
return "sv";
case TRANSLATION_LANG_HR:
return "hr";
case TRANSLATION_LANG_KO:
return "ko";
case TRANSLATION_LANG_ZH_CN:
return "zh-CN";
case TRANSLATION_LANG_ZH_TW:
return "zh-TW";
case TRANSLATION_LANG_CA:
return "ca";
case TRANSLATION_LANG_BG:
return "bg";
case TRANSLATION_LANG_BN:
return "bn";
case TRANSLATION_LANG_EU:
return "eu";
case TRANSLATION_LANG_AZ:
return "az";
case TRANSLATION_LANG_AR:
return "ar";
case TRANSLATION_LANG_AST:
return "ast";
case TRANSLATION_LANG_SQ:
return "sq";
case TRANSLATION_LANG_AF:
return "af";
case TRANSLATION_LANG_EO:
return "eo";
case TRANSLATION_LANG_ET:
return "et";
case TRANSLATION_LANG_TL:
return "tl";
case TRANSLATION_LANG_FI:
return "fi";
case TRANSLATION_LANG_GL:
return "gl";
case TRANSLATION_LANG_KA:
return "ka";
case TRANSLATION_LANG_EL:
return "el";
case TRANSLATION_LANG_GU:
return "gu";
case TRANSLATION_LANG_HT:
return "ht";
case TRANSLATION_LANG_HE:
return "he";
case TRANSLATION_LANG_HI:
return "hi";
case TRANSLATION_LANG_HU:
return "hu";
case TRANSLATION_LANG_IS:
return "is";
case TRANSLATION_LANG_ID:
return "id";
case TRANSLATION_LANG_GA:
return "ga";
case TRANSLATION_LANG_KN:
return "kn";
case TRANSLATION_LANG_LA:
return "la";
case TRANSLATION_LANG_LV:
return "lv";
case TRANSLATION_LANG_LT:
return "lt";
case TRANSLATION_LANG_MK:
return "mk";
case TRANSLATION_LANG_MS:
return "ms";
case TRANSLATION_LANG_MT:
return "mt";
case TRANSLATION_LANG_NO:
return "no";
case TRANSLATION_LANG_FA:
return "fa";
case TRANSLATION_LANG_PL:
return "pl";
case TRANSLATION_LANG_PT:
return "pt";
case TRANSLATION_LANG_RO:
return "ro";
case TRANSLATION_LANG_RU:
return "ru";
case TRANSLATION_LANG_SR:
return "sr";
case TRANSLATION_LANG_SK:
return "sk";
case TRANSLATION_LANG_SL:
return "sl";
case TRANSLATION_LANG_SW:
return "sw";
case TRANSLATION_LANG_TA:
return "ta";
case TRANSLATION_LANG_TE:
return "te";
case TRANSLATION_LANG_TH:
return "th";
case TRANSLATION_LANG_TR:
return "tr";
case TRANSLATION_LANG_UK:
return "uk";
case TRANSLATION_LANG_BE:
return "be";
case TRANSLATION_LANG_UR:
return "ur";
case TRANSLATION_LANG_VI:
return "vi";
case TRANSLATION_LANG_CY:
return "cy";
case TRANSLATION_LANG_YI:
return "yi";
case TRANSLATION_LANG_DONT_CARE:
case TRANSLATION_LANG_LAST:
break;
}
return "";
}
/** /**
* Returns true if the accessibility narrator is currently playing audio. * Returns true if the accessibility narrator is currently playing audio.
*/ */
@ -156,8 +301,9 @@ static void accessibility_speak(const char *text)
settings_t *settings = config_get_ptr(); settings_t *settings = config_get_ptr();
unsigned speed = settings->uints.accessibility_narrator_speech_speed; unsigned speed = settings->uints.accessibility_narrator_speech_speed;
bool narrator_on = settings->bools.accessibility_enable; bool narrator_on = settings->bools.accessibility_enable;
const char* voice = ai_service_get_str(settings->uints.ai_service_target_lang);
accessibility_speak_priority(narrator_on, speed, text, 10); navigation_say(narrator_on, speed, text, 10);
#endif #endif
} }
@ -175,10 +321,11 @@ static void translation_speak(const char *text)
unsigned mode = settings->uints.ai_service_mode; unsigned mode = settings->uints.ai_service_mode;
unsigned speed = settings->uints.accessibility_narrator_speech_speed; unsigned speed = settings->uints.accessibility_narrator_speech_speed;
bool narrator_on = settings->bools.accessibility_enable; bool narrator_on = settings->bools.accessibility_enable;
const char* voice = ai_service_get_str(settings->uints.ai_service_target_lang);
/* Force the use of the narrator in Narrator modes (TTS) */ /* Force the use of the narrator in Narrator modes (TTS) */
if (mode == 2 || mode == 4 || mode == 5 || narrator_on || access_st->enabled) if (mode == 2 || mode == 4 || mode == 5 || narrator_on || access_st->enabled)
accessibility_speak_priority(true, speed, text, 10); accessibility_speak_priority(speed, text, 10, voice);
#endif #endif
} }
@ -329,151 +476,6 @@ void translation_release(bool inform)
translation_hash_info(MSG_AI_AUTO_MODE_DISABLED); translation_hash_info(MSG_AI_AUTO_MODE_DISABLED);
} }
/**
* Returns the string representation of the translation language enum value.
*/
static const char* ai_service_get_str(enum translation_lang id)
{
switch (id)
{
case TRANSLATION_LANG_EN:
return "en";
case TRANSLATION_LANG_ES:
return "es";
case TRANSLATION_LANG_FR:
return "fr";
case TRANSLATION_LANG_IT:
return "it";
case TRANSLATION_LANG_DE:
return "de";
case TRANSLATION_LANG_JP:
return "ja";
case TRANSLATION_LANG_NL:
return "nl";
case TRANSLATION_LANG_CS:
return "cs";
case TRANSLATION_LANG_DA:
return "da";
case TRANSLATION_LANG_SV:
return "sv";
case TRANSLATION_LANG_HR:
return "hr";
case TRANSLATION_LANG_KO:
return "ko";
case TRANSLATION_LANG_ZH_CN:
return "zh-CN";
case TRANSLATION_LANG_ZH_TW:
return "zh-TW";
case TRANSLATION_LANG_CA:
return "ca";
case TRANSLATION_LANG_BG:
return "bg";
case TRANSLATION_LANG_BN:
return "bn";
case TRANSLATION_LANG_EU:
return "eu";
case TRANSLATION_LANG_AZ:
return "az";
case TRANSLATION_LANG_AR:
return "ar";
case TRANSLATION_LANG_AST:
return "ast";
case TRANSLATION_LANG_SQ:
return "sq";
case TRANSLATION_LANG_AF:
return "af";
case TRANSLATION_LANG_EO:
return "eo";
case TRANSLATION_LANG_ET:
return "et";
case TRANSLATION_LANG_TL:
return "tl";
case TRANSLATION_LANG_FI:
return "fi";
case TRANSLATION_LANG_GL:
return "gl";
case TRANSLATION_LANG_KA:
return "ka";
case TRANSLATION_LANG_EL:
return "el";
case TRANSLATION_LANG_GU:
return "gu";
case TRANSLATION_LANG_HT:
return "ht";
case TRANSLATION_LANG_HE:
return "he";
case TRANSLATION_LANG_HI:
return "hi";
case TRANSLATION_LANG_HU:
return "hu";
case TRANSLATION_LANG_IS:
return "is";
case TRANSLATION_LANG_ID:
return "id";
case TRANSLATION_LANG_GA:
return "ga";
case TRANSLATION_LANG_KN:
return "kn";
case TRANSLATION_LANG_LA:
return "la";
case TRANSLATION_LANG_LV:
return "lv";
case TRANSLATION_LANG_LT:
return "lt";
case TRANSLATION_LANG_MK:
return "mk";
case TRANSLATION_LANG_MS:
return "ms";
case TRANSLATION_LANG_MT:
return "mt";
case TRANSLATION_LANG_NO:
return "no";
case TRANSLATION_LANG_FA:
return "fa";
case TRANSLATION_LANG_PL:
return "pl";
case TRANSLATION_LANG_PT:
return "pt";
case TRANSLATION_LANG_RO:
return "ro";
case TRANSLATION_LANG_RU:
return "ru";
case TRANSLATION_LANG_SR:
return "sr";
case TRANSLATION_LANG_SK:
return "sk";
case TRANSLATION_LANG_SL:
return "sl";
case TRANSLATION_LANG_SW:
return "sw";
case TRANSLATION_LANG_TA:
return "ta";
case TRANSLATION_LANG_TE:
return "te";
case TRANSLATION_LANG_TH:
return "th";
case TRANSLATION_LANG_TR:
return "tr";
case TRANSLATION_LANG_UK:
return "uk";
case TRANSLATION_LANG_BE:
return "be";
case TRANSLATION_LANG_UR:
return "ur";
case TRANSLATION_LANG_VI:
return "vi";
case TRANSLATION_LANG_CY:
return "cy";
case TRANSLATION_LANG_YI:
return "yi";
case TRANSLATION_LANG_DONT_CARE:
case TRANSLATION_LANG_LAST:
break;
}
return "";
}
/* AUTOMATION --------------------------------------------------------------- */ /* AUTOMATION --------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */