Ecore_Timer *delay_timer;
bool request_speak_do;
bool running;
+ int tts_speed_max;
+ int tts_speed_normal;
+ int tts_speed_min;
} TWData;
/* Forward delcarations - begin */
return v;
}
+static int choose_speed()
+{
+ int speed = vc_get_tts_speed();
+ return tw_is_speed_in_range(speed) ? speed : TTS_SPEED_AUTO;
+}
+
static const char *get_tts_error(int r)
{
switch (r) {
{
DEBUG("Passing TEXT: %s to TTS", utf8_line);
int utterance_id;
- int ret = tts_add_text(tw->tts, utf8_line, NULL, TTS_VOICE_TYPE_AUTO, TTS_SPEED_AUTO, &utterance_id);
+
+ int ret = tts_add_text(tw->tts, utf8_line, vc_get_tts_language(), vc_get_tts_voice_type(), choose_speed(), &utterance_id);
if (ret) {
tts_state_e state = -1;
switch (ret) {
tw->request_speak_do = false;
tw->running = false;
+ if (tts_get_speed_range(tw->tts, &(tw->tts_speed_min), &(tw->tts_speed_normal), &(tw->tts_speed_max)) != 0) {
+ tw->tts_speed_min = tw->tts_speed_normal = tw->tts_speed_max = TTS_SPEED_AUTO;
+ }
+
r = tts_set_mode(tw->tts, TTS_MODE_SCREEN_READER);
DEBUG("Set tts mode SR %d (%s)", r, get_tts_error(r));
ERROR("Fail to set utterance completed cb (%s)", get_tts_error(r));
}
- tw->init = 1;
+ tw->init = 1; // Set now so that the below line does not cause infinite recursion
+ vc_tts_voice_validator_register(&tw_is_voice_supported);
+
DEBUG("---------------------- TTS_init END ----------------------\n\n");
}
void tw_shutdown(void)
{
+ vc_tts_voice_validator_register(NULL);
+
TWData *tw = tw_get_instance();
stop_speaking(tw);
tts_destroy(tw->tts);
}
return EINA_TRUE;
}
+
+struct SupportedVoiceCallbackData {
+ const char *const language; // in
+ const int voice_type; //in
+ Eina_Bool is_supported; // out
+};
+
+static bool supported_voice_callback(tts_h tts, const char *language, int voice_type, void *user_data)
+{
+ struct SupportedVoiceCallbackData *data = user_data;
+ bool keep_going = true;
+
+ if (!g_strcmp0(language, data->language) && (data->voice_type == 0 || voice_type == data->voice_type)) {
+ data->is_supported = EINA_TRUE;
+ keep_going = false;
+ }
+
+ return keep_going;
+}
+
+Eina_Bool tw_is_voice_supported(const char *language, int voice_type)
+{
+ if (!language || voice_type < 0)
+ return EINA_FALSE;
+
+ TWData *tw = tw_get_instance();
+ struct SupportedVoiceCallbackData data = {
+ .language = language,
+ .voice_type = voice_type,
+ .is_supported = EINA_FALSE,
+ };
+
+ int status = tts_foreach_supported_voices(tw->tts, &supported_voice_callback, &data);
+ if (status != 0)
+ ERROR("tts_foreach_supported_voices failed with code %d", status);
+
+ if (data.is_supported)
+ DEBUG("Language %s with voice_type %d is supported", language, voice_type);
+ else
+ WARNING("Language %s with voice_type %d is NOT supported", language, voice_type);
+
+ return data.is_supported;
+}
+
+Eina_Bool tw_is_speed_in_range(int speed)
+{
+ TWData *tw = tw_get_instance();
+ Eina_Bool in_range = tw->tts_speed_min <= speed && speed <= tw->tts_speed_max;
+ if (!in_range)
+ DEBUG("Speed %d is not in range (%d, %d)", speed, tw->tts_speed_min, tw->tts_speed_max);
+ return in_range;
+}
#define VCKEY_HAPTIC "db/setting/accessibility/screen_reader/haptic"
#define VCKEY_KEYBOARD_FEEDBACK "db/setting/accessibility/screen_reader/keyboard_feedback"
#define VCKEY_SOUND_FEEDBACK "db/setting/accessibility/screen_reader/sound_feedback"
+#define VCKEY_TTS_VOICE "db/setting/accessibility/screen_reader/tts_voice"
+#define VCKEY_TTS_SPEED "db/setting/accessibility/screen_reader/tts_speed"
#define VCKEY_LCD_BACKLIGHT_NORMAL "db/setting/lcd_backlight_normal"
typedef struct {
int keyboard_feedback;
int sound_feedback;
int lcd_backlight_timeout;
+ int tts_speed;
+ int tts_voice_type;
+ char *tts_language;
+ TTSVoiceValidatorCb tts_voice_validator_cb;
} VConfData;
+static VConfData *vc_get_instance(void);
+
static int display_language_changed_cb(void *event_info, void *data)
{
DEBUG("START");
DEBUG("END");
}
+static void vcwrap_update_derived_fields(void *destination)
+{
+ VConfData *vconf_data = vc_get_instance();
+ if (destination == &(vconf_data->tts_language)) {
+ const char *voice_type = ""; // Pointer to '\0'
+ char *separator = vconf_data->tts_language ? strchr(vconf_data->tts_language, ':') : NULL;
+ if (separator) {
+ *separator = '\0'; // Cut the string short
+ voice_type = separator + 1; // Valid even if ':' was the last char -- then points to '\0'
+ }
+
+ vconf_data->tts_voice_type = atoi(voice_type); // atoi() returns 0 on failure
+
+ if (vconf_data->tts_voice_validator_cb && !vconf_data->tts_voice_validator_cb(vconf_data->tts_language, vconf_data->tts_voice_type)) {
+ free(vconf_data->tts_language);
+ // fallback to auto-selection of platform default language and voice_type in case customized values are not valid
+ vconf_data->tts_language = NULL;
+ vconf_data->tts_voice_type = 0;
+ }
+ }
+}
+
static int vcwrap_get_key_int(const char *key, int def)
{
int result = def;
return result;
}
+// Note: free() the result in either case
+static char *vcwrap_get_key_str(const char *key, const char *def)
+{
+ char *result = vconf_get_str(key); // already strdup()'ed
+ if (!result) {
+ ERROR("vconf_get_str failed! key=%s", key);
+ return def ? strdup(def) : NULL;
+ }
+ return result;
+}
+
static void vcwrap_field_updater_int(keynode_t *node, void *destination)
{
if (!destination)
return;
*((int*)destination) = vconf_keynode_get_int(node);
+ vcwrap_update_derived_fields(destination);
+}
+
+static void vcwrap_field_updater_str(keynode_t *node, void *destination)
+{
+ if (!destination)
+ return;
+
+ const char *node_str = vconf_keynode_get_str(node);
+ char **dest_ptr = (char **)destination;
+
+ free(*dest_ptr);
+ *dest_ptr = strdup(node_str);
+ vcwrap_update_derived_fields(destination);
}
static void vcwrap_set_field_updater_int(const char *key, int *data)
ERROR("Could not create updater for key=%s", key);
}
+static void vcwrap_set_field_updater_str(const char *key, char **data)
+{
+ if (vconf_notify_key_changed(key, vcwrap_field_updater_str, (void *)data))
+ ERROR("Could not create updater for key=%s", key);
+}
+
static void vcwrap_unset_field_updater_int(const char *key)
{
if (vconf_ignore_key_changed(key, vcwrap_field_updater_int))
DEBUG("Could not delete notify callback for key=%s", key);
}
+static void vcwrap_unset_field_updater_str(const char *key)
+{
+ if (vconf_ignore_key_changed(key, vcwrap_field_updater_str))
+ DEBUG("Could not delete notify callback for key=%s", key);
+}
+
static VConfData *vc_get_instance(void)
{
static VConfData vconf_data = {0};
vconf_data.keyboard_feedback = vcwrap_get_key_int(VCKEY_KEYBOARD_FEEDBACK, true);
vconf_data.sound_feedback = vcwrap_get_key_int(VCKEY_SOUND_FEEDBACK, true);
vconf_data.lcd_backlight_timeout = vcwrap_get_key_int(VCKEY_LCD_BACKLIGHT_NORMAL, -1);
+ vconf_data.tts_speed = vcwrap_get_key_int(VCKEY_TTS_SPEED, 0);
+ vconf_data.tts_language = vcwrap_get_key_str(VCKEY_TTS_VOICE, NULL);
+ vcwrap_update_derived_fields(&(vconf_data.tts_language));
vc_get_key_values();
vcwrap_set_field_updater_int(VCKEY_KEYBOARD_FEEDBACK, &(vconf_data.keyboard_feedback));
vcwrap_set_field_updater_int(VCKEY_SOUND_FEEDBACK, &(vconf_data.sound_feedback));
vcwrap_set_field_updater_int(VCKEY_LCD_BACKLIGHT_NORMAL, &(vconf_data.lcd_backlight_timeout));
+ vcwrap_set_field_updater_int(VCKEY_TTS_SPEED, &(vconf_data.tts_speed));
+ vcwrap_set_field_updater_str(VCKEY_TTS_VOICE, &(vconf_data.tts_language));
DEBUG("---------------------- VCONF_init END ----------------------\n\n");
vcwrap_unset_field_updater_int(VCKEY_DESCRIPTION);
vcwrap_unset_field_updater_int(VCKEY_SOUND_FEEDBACK);
vcwrap_unset_field_updater_int(VCKEY_LCD_BACKLIGHT_NORMAL);
+ vcwrap_unset_field_updater_int(VCKEY_TTS_SPEED);
+ vcwrap_unset_field_updater_str(VCKEY_TTS_VOICE);
+
+ free(vconf_data->tts_language);
+ vconf_data->tts_language = NULL;
}
int vc_get_read_description(void)
return vc_get_instance()->lcd_backlight_timeout;
}
+int vc_get_tts_speed(void)
+{
+ return vc_get_instance()->tts_speed;
+}
+
+int vc_get_tts_voice_type(void)
+{
+ return vc_get_instance()->tts_voice_type;
+}
+
+const char *vc_get_tts_language(void)
+{
+ return vc_get_instance()->tts_language;
+}
+
+void vc_tts_voice_validator_register(TTSVoiceValidatorCb cb)
+{
+ VConfData *vconf_data = vc_get_instance();
+
+ vconf_data->tts_voice_validator_cb = cb;
+ if (cb)
+ vcwrap_update_derived_fields(&(vconf_data->tts_language));
+}
\ No newline at end of file