Emacspeak on Android
by Tim Makarios
Hi,
I've been trying to get Emacspeak working on my mobile phone, which is
running LineageOS 17.1 (based on Android 10). So far, I've tried a couple
of things, but neither has quite worked.
First, I tried installing Emacspeak on debian-buster from proot-distro in
Termux. Unfortunately, it's silent, and running espeak on its own results
in a long list of errors, starting with
> ALSA lib confmisc.c:767:(parse_card) cannot find card '0'
so I guess Termux's proot-distro isn't giving Debian access to the audio.
For another attempt, I tried building Emacspeak directly in Termux. This
required building TclX, too, which seemed to work, but when I tried running
`./servers/espeak` it gave these errors
> couldn't load file
> "/data/data/com.termux/files/usr/lib/tclx8.4/libtclx8.4.so": dlopen
> failed: cannot locate symbol "rresvport" referenced by
> "/data/data/com.termux/files/usr/lib/tclx8.4/libtclx8.4.so"...
> while executing
> "load /data/data/com.termux/files/usr/lib/tclx8.4/libtclx8.4.so Tclx"
> ("package ifneeded Tclx 8.4" script)
> invoked from within
> "package require Tclx"
> (file "./servers/espeak" line 37)
so I guess TclX relies on rresvport, whatever that is, and Termux doesn't
provide it.
Does anyone have any clues about how I might be able to get Emacspeak
running on my phone?
Thanks,
Tim
<><
1 year
Latest espeak-ng from master is silent with emacspeak, but works with speech-dispatcher
by Zahari Yurukov
Hi,
I'm using the latest emacs from master, as well as the latest emacspeak from master. Fedora 33 with the latest updates.
I compiled the latest espeak-ng from master with sonic support enabled. It works fine with Orca via speech-dispatcher, but emacspeak is silent without giving any errors.
Until that the espeak-ng package from Fedora's repositories worked fine with emacspeak. I've priviously have used espeak-ng compiled by myself with the exact same configuration without any problem with emacspeak.
Emacspeak's Auditory Icons do work. I've tried the following from a separate console and I can hear espeak-ng:
speak-ng -s 1000 --stdout hello |play -
I recompiled the eemacspeak's espeak module with :
make -B espeak
Can you reproduce this? Do you have any clue where the problem could be.
--
Best wishes,
Zahari
1 year, 5 months
Emacspeak fails to start under WSL2
by Bill Dengler
Hello,
Trying to run Emacs 27.1 with Emacspeak commit 3b35ec3 under Debian 11
Bullseye on Windows 11 through WSL2.
Both the Espeak and Outloud speech servers produce speech when running via
the shell, but when I try and start Emacs the start-up icon plays for about
a second and then Emacs hangs.
My minimal init.el is as follows:
(setenv "DTK_PROGRAM" "outloud")
(load-file "/home/codeofdusk/.emacs.d/emacspeak/lisp/emacspeak-setup.el")
Any suggestions to help get this going?
Thanks in advance,
Bill
1 year, 6 months
[PATCH] Add dtk-set-variant command to allow espeak's variants to be selected
by Krzysztof Drewniak
dtk-set-variant is bound to C-e d M-v.
(In addition, fix indentation in servers/espeak and fix close_tags in
tclsespeak.cpp to be more memory efficient).
---
info/docs.texi | 25 +++++-
info/tts.texi | 9 +++
lisp/dtk-speak.el | 18 +++++
lisp/emacspeak-keymap.el | 1 +
servers/espeak | 42 ++++++++---
servers/native-espeak/tclespeak.cpp | 113 +++++++++++++++++-----------
6 files changed, 156 insertions(+), 52 deletions(-)
diff --git a/info/docs.texi b/info/docs.texi
index ac3a3e908..35cbf4a68 100644
--- a/info/docs.texi
+++ b/info/docs.texi
@@ -4,7 +4,7 @@
@include intro-docs.texi
-This chapter documents a total of 1144 commands and 146 options.
+This chapter documents a total of 1145 commands and 146 options.
@menu
* amixer::Control AMixer from Emacs.
@@ -732,6 +732,29 @@ current local value to the result.
@end format
@end deffn
+@subsection dtk-set-variant
+@deffn {Command} dtk-set-variant (&optional variant)
+
+@table @kbd
+@item C-e d M-v
+@kindex C-e d M-v
+@item <fn> d M-v
+@kindex <fn> d M-v
+@end table
+
+@findex dtk-set-variant
+@format
+Set voice variant used by the speech engine to VARIANT.
+
+VARIANT may be any value supported by the speech engine.
+In the case of espeak, it may be a string name or an integer ID.
+
+Omitting VARIANT or setting it to NIL resetss this value to default.
+
+(fn &optional VARIANT)
+@end format
+@end deffn
+
@subsubsection dtk-stop
@deffn {Command} dtk-stop (&optional all)
@table @kbd
diff --git a/info/tts.texi b/info/tts.texi
index c856ec658..52a6f19c2 100644
--- a/info/tts.texi
+++ b/info/tts.texi
@@ -130,6 +130,15 @@ Dectalks, e.g. the Dectalk Express. Possible
values are `math, name,
europe, spell', all of which can be turned on or off. Argument STATE
specifies new state.
+@findex dtk-set-variant
+@kindex C-e d M-v
+@item C-e d M-v
+@code{dtk-set-variant}
+
+Set the voice variant used by the speech engine, if the engine has
+a concept of variants, to VARIANT. If VARIANT is blank or nil, resets
+the variant setting to its default value.
+
@findex dtk-toggle-split-caps
@kindex C-e d s
@item C-e d s
diff --git a/lisp/dtk-speak.el b/lisp/dtk-speak.el
index e0b34ac49..a0c1f9c93 100644
--- a/lisp/dtk-speak.el
+++ b/lisp/dtk-speak.el
@@ -826,6 +826,16 @@ then set the current local value to the result."
dtk-character-scale
(if prefix "" "locally")))))
+(defun dtk-set-variant (&optional variant)
+ "Set voice variant used by the speech engine to VARIANT.
+
+VARIANT may be any value supported by the speech engine.
+In the case of espeak, it may be a string name or an integer ID.
+
+Omitting VARIANT or setting it to NIL resetss this value to default."
+ (interactive "sVariant name (blank selects default): ")
+ (dtk-interp-set-variant variant (called-interactively-p 'interactive)))
+
(ems-generate-switcher
'dtk-toggle-quiet
'dtk-quiet
@@ -1892,6 +1902,14 @@ Notification is logged in the notifications
buffer unless `dont-log' is T. "
dtk-speaker-process
(format "tts_set_punctuations %s\nd\n" mode)))
+;;}}}
+;;{{{ variant
+(defsubst dtk-interp-set-variant (variant say-it)
+ (cl-declare (special dtk-speaker-process))
+ (process-send-string
+ dtk-speaker-process
+ (format "tts_set_variant %S %S" variant say-it)))
+
;;}}}
;;{{{ reset
diff --git a/lisp/emacspeak-keymap.el b/lisp/emacspeak-keymap.el
index 83a46a086..24eecf9ee 100644
--- a/lisp/emacspeak-keymap.el
+++ b/lisp/emacspeak-keymap.el
@@ -359,6 +359,7 @@
("C-n" dtk-notify-initialize)
("C-o" outloud)
("C-v" global-voice-lock-mode)
+ ("M-v" dtk-set-variant)
("d" dtk-select-server)
("L" dtk-local-server)
("N" dtk-set-next-language)
diff --git a/servers/espeak b/servers/espeak
index 3f19451d7..f20cab5f7 100755
--- a/servers/espeak
+++ b/servers/espeak
@@ -56,6 +56,10 @@ source $wd/tts-lib.tcl
# For example, if there are three available languages:
# langsynth(top)=2
+# langsynth(variant): name (or ID) of the language variant in use
+# If unset, the server defaults to an autoselected male variant
+# Set by the application
+
# voicename: name of the current voice for announcements
# This variable is set by tclespeak
@@ -128,7 +132,7 @@ proc set_previous_lang {say_it} {
set langsynth(current) $index
set langcode(current) $langcode($index)
setLanguage $langsynth(current)
-puts stderr "Language: $langsynth(current) Voice: $voicename"
+ puts stderr "Language: $langsynth(current) Voice: $voicename"
if { [info exists say_it]} {
tts_say "$voicename "
}
@@ -140,15 +144,15 @@ proc set_lang {{name "en"} {say_it "nil"}} {
global langsynth
global langalias
global langcode
-global voicename
- if { ![info exists langalias($name)]} {
- return
- }
+ global voicename
+ if { ![info exists langalias($name)]} {
+ return
+ }
- if { $langalias($name) == $langsynth(current) } {
+ if { $langalias($name) == $langsynth(current) } {
return
- }
-
+ }
+
set langsynth(current) $langalias($name)
set langcode(current) $langcode($langalias($name))
setLanguage $langsynth(current)
@@ -169,6 +173,26 @@ proc set_preferred_lang {alias lang} {
set langalias($alias) $langalias($lang)
}
+# tts_set_variant - clears the variant
+# tts_set_variant "victor" - sets the variant
+# tts_set_variant 1 - sets the variant by index
+proc tts_set_variant {{new_variant ""} {say_it "nil"}} {
+ global langsynth
+ global voicename
+ set new_variant [string trim $new_variant]
+ if { $new_variant == "" || $new_variant == "nil" } {
+ unset langsynth(variant)
+ } else {
+ set langsynth(variant) $new_variant
+ }
+ # Re-set the current voice, having updated the variant
+ setLanguage $langsynth(current)
+ if { $say_it != "nil" } {
+ tts_say "$voicename "
+ }
+
+}
+
#debug
proc list_lang {} {
global langcode
@@ -198,7 +222,7 @@ proc tts_set_punctuations {mode} {
proc tts_set_speech_rate {rate} {
global tts
- set factor $tts(char_factor)
+ set factor $tts(char_factor)
set tts(speech_rate) $rate
setRate 0 $rate
service
diff --git a/servers/native-espeak/tclespeak.cpp
b/servers/native-espeak/tclespeak.cpp
index 4c29f1a98..790daf30d 100644
--- a/servers/native-espeak/tclespeak.cpp
+++ b/servers/native-espeak/tclespeak.cpp
@@ -39,12 +39,13 @@
#include <assert.h>
#include <espeak-ng/speak_lib.h>
+#include <set>
+#include <sstream>
#include <stdlib.h>
#include <string.h>
+#include <string>
#include <sys/time.h>
#include <tcl.h>
-#include <set>
-#include <string>
#include <vector>
using std::set;
using std::string;
@@ -74,7 +75,7 @@ int Synchronize(ClientData, Tcl_Interp *, int, Tcl_Obj
*CONST[]);
int Pause(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST[]);
int Resume(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST[]);
-static void initLanguage(Tcl_Interp *interp);
+static int initLanguage(Tcl_Interp *interp);
static int getLangIndex(Tcl_Interp *interp, unsigned long *theIndex);
//>
@@ -121,8 +122,7 @@ int Tclespeak_Init(Tcl_Interp *interp) {
TclEspeakFree);
//>
- initLanguage(interp);
- return TCL_OK;
+ return initLanguage(interp);
}
int GetRate(ClientData handle, Tcl_Interp *interp, int objc,
@@ -166,17 +166,13 @@ int SetRate(ClientData handle, Tcl_Interp *interp,
int objc,
//>
//<say
-static bool closeTags(string input, string &output) {
- char *tag_orig = (char *)malloc(sizeof(char) * (input.size() + 1));
- strncpy(tag_orig, input.c_str(), input.size());
- output = "";
-
+static bool closeTags(const string input, string &output) {
+ std::ostringstream closingTags;
// check that a text (non whitespace) is present
- char *tag = tag_orig;
int a_tag_count = 0;
bool a_text_is_present = false;
- while (*tag) {
+ for (auto tag = input.cbegin(); tag != input.cend(); ++tag) {
if (*tag == '<') {
a_tag_count++;
}
@@ -188,31 +184,33 @@ static bool closeTags(string input, string &output) {
if ((*tag == '>') && a_tag_count) {
a_tag_count--;
}
- tag++;
}
if (a_text_is_present) {
- tag = tag_orig;
- while (tag) {
+ string::size_type tag_pos = input.size();
+ if (string::npos == tag_pos) {
+ fprintf(stderr, "Synthesizer argument of size (size_t)(-1),
ignoring "
+ "last chraracter\n");
+ --tag_pos;
+ }
+ while (string::npos != tag_pos) {
// look for a '<'
- tag = strrchr(tag_orig, '<');
-
- if (tag) {
- char *end = strchr(tag, ' ');
- if (!end && (NULL == strchr(tag, '/'))) {
- end = strchr(tag, '>');
+ tag_pos = input.find_last_of('<', tag_pos);
+ if (string::npos != tag_pos) {
+ string::size_type end = input.find_first_of(' ', tag_pos);
+ if ((string::npos != end) &&
+ (string::npos == input.find_first_of('/', tag_pos))) {
+ end = input.find_first_of('>', tag_pos);
}
- if (end && (tag + 1 < end)) {
- *end = 0;
- output += "</" + string(tag + 1) + ">";
+ if ((string::npos != end) && (tag_pos + 1 < end)) {
+ closingTags << "</" << input.substr(tag_pos + 1, end) << ">";
}
- *tag = 0;
+ tag_pos--; // Start search before previous tag to avoid
infinite loop
}
}
}
- free(tag_orig);
-
+ output.assign(closingTags.str());
return a_text_is_present;
}
@@ -228,8 +226,14 @@ int Say(ClientData handle, Tcl_Interp *interp, int
objc,
string a_ssml = a_begin_ssml + a_end_ssml;
unsigned int unique_identifier = 0;
- espeak_Synth(a_ssml.c_str(), a_ssml.length() + 1, 0,
POS_CHARACTER, 0,
- espeakCHARS_UTF8 | espeakSSML, &unique_identifier,
NULL);
+ if (EE_OK != espeak_Synth(a_ssml.c_str(), a_ssml.length() + 1, 0,
+ POS_CHARACTER, 0,
+ espeakCHARS_UTF8 | espeakSSML,
+ &unique_identifier, NULL)) {
+ Tcl_AppendResult(
+ interp, "Could not synthesize string: ", a_ssml.c_str(),
NULL);
+ return TCL_ERROR;
+ }
}
}
}
@@ -363,17 +367,37 @@ int getTTSVersion(ClientData handle, Tcl_Interp
*interp, int objc,
static vector<string> available_languages;
-static void SetLanguageHelper(Tcl_Interp *interp, size_t aIndex) {
- espeak_VOICE *current_voice = NULL;
- espeak_VOICE a_voice;
- memset(&a_voice, 0, sizeof(espeak_VOICE));
- a_voice.languages = (char *)available_languages[aIndex].c_str();
- a_voice.gender = 1;
- espeak_SetVoiceByProperties(&a_voice);
- current_voice = espeak_GetCurrentVoice();
+static int SetLanguageHelper(Tcl_Interp *interp, size_t aIndex) {
+ espeak_ERROR voice_status = espeak_ERROR::EE_OK;
+ Tcl_Obj *variant_name = Tcl_GetVar2Ex(interp, "langsynth", "variant", 0);
+ if (variant_name) {
+ int variant_name_len = 0;
+ char *variant_name_data =
+ Tcl_GetStringFromObj(variant_name, &variant_name_len);
+ string variant_name_str(variant_name_data, variant_name_len);
+ string name = available_languages[aIndex] + "+" + variant_name_str;
+ voice_status = espeak_SetVoiceByName(name.c_str());
+ if (espeak_ERROR::EE_OK != voice_status) {
+ fprintf(stderr,
+ "Could not load voice %s, falling back to language-based
search",
+ name.c_str());
+ }
+ }
+
+ if (!variant_name || espeak_ERROR::EE_OK != voice_status) {
+ espeak_VOICE a_voice;
+ memset(&a_voice, 0, sizeof(espeak_VOICE));
+ a_voice.languages = (char *)available_languages[aIndex].c_str();
+ a_voice.gender = 1;
+ voice_status = espeak_SetVoiceByProperties(&a_voice);
+ }
+ if (espeak_ERROR::EE_OK != voice_status) {
+ Tcl_AppendResult(interp, "could not set voice");
+ return TCL_ERROR;
+ }
+ espeak_VOICE *current_voice = espeak_GetCurrentVoice();
Tcl_SetVar(interp, "voicename", current_voice->name, 0);
- // But what if we couldn't set the voice? Need some better error
handling.
- return;
+ return TCL_OK;
}
int SetLanguage(ClientData eciHandle, Tcl_Interp *interp, int objc,
@@ -381,8 +405,9 @@ int SetLanguage(ClientData eciHandle, Tcl_Interp
*interp, int objc,
unsigned long aIndex = 0;
if (getLangIndex(interp, &aIndex)) {
- SetLanguageHelper(interp, aIndex);
+ return SetLanguageHelper(interp, aIndex);
}
+ // TODO: Error reporting for this
return TCL_OK;
}
@@ -404,7 +429,7 @@ static vector<string> ParseLanguages(const char
*lang_str) {
return voice_langs;
}
-static void initLanguage(Tcl_Interp *interp) {
+static int initLanguage(Tcl_Interp *interp) {
// List the available languages
set<string> unique_languages;
int i = 0;
@@ -471,11 +496,15 @@ static void initLanguage(Tcl_Interp *interp) {
Tcl_SetVar2(interp, "langsynth", "current", buffer, 0);
Tcl_SetVar2(interp, "langcode", "current", "en", 0);
}
- SetLanguageHelper(interp, default_index);
+
+ if (TCL_OK != SetLanguageHelper(interp, default_index)) {
+ return TCL_ERROR;
+ }
// Presumably we have at least one language, namely English,
// so no chance of underflowing size_t with this subtraction:
snprintf(buffer, sizeof(buffer), "%lu", lang_count - 1);
Tcl_SetVar2(interp, "langsynth", "top", buffer, 0);
+ return TCL_OK;
}
static int getLangIndex(Tcl_Interp *interp, unsigned long *theIndex) {
--
2.25.1
dtk-set-variant is bound to C-e d M-v.
(In addition, fix indentation in servers/espeak and fix close_tags in
tclsespeak.cpp to be more memory efficient).
---
info/docs.texi | 25 +++++-
info/tts.texi | 9 +++
lisp/dtk-speak.el | 18 +++++
lisp/emacspeak-keymap.el | 1 +
servers/espeak | 42 ++++++++---
servers/native-espeak/tclespeak.cpp | 113 +++++++++++++++++-----------
6 files changed, 156 insertions(+), 52 deletions(-)
diff --git a/info/docs.texi b/info/docs.texi
index ac3a3e908..35cbf4a68 100644
--- a/info/docs.texi
+++ b/info/docs.texi
@@ -4,7 +4,7 @@
@include intro-docs.texi
-This chapter documents a total of 1144 commands and 146 options.
+This chapter documents a total of 1145 commands and 146 options.
@menu
* amixer::Control AMixer from Emacs.
@@ -732,6 +732,29 @@ current local value to the result.
@end format
@end deffn
+@subsection dtk-set-variant
+@deffn {Command} dtk-set-variant (&optional variant)
+
+@table @kbd
+@item C-e d M-v
+@kindex C-e d M-v
+@item <fn> d M-v
+@kindex <fn> d M-v
+@end table
+
+@findex dtk-set-variant
+@format
+Set voice variant used by the speech engine to VARIANT.
+
+VARIANT may be any value supported by the speech engine.
+In the case of espeak, it may be a string name or an integer ID.
+
+Omitting VARIANT or setting it to NIL resetss this value to default.
+
+(fn &optional VARIANT)
+@end format
+@end deffn
+
@subsubsection dtk-stop
@deffn {Command} dtk-stop (&optional all)
@table @kbd
diff --git a/info/tts.texi b/info/tts.texi
index c856ec658..52a6f19c2 100644
--- a/info/tts.texi
+++ b/info/tts.texi
@@ -130,6 +130,15 @@ Dectalks, e.g. the Dectalk Express. Possible
values are `math, name,
europe, spell', all of which can be turned on or off. Argument STATE
specifies new state.
+@findex dtk-set-variant
+@kindex C-e d M-v
+@item C-e d M-v
+@code{dtk-set-variant}
+
+Set the voice variant used by the speech engine, if the engine has
+a concept of variants, to VARIANT. If VARIANT is blank or nil, resets
+the variant setting to its default value.
+
@findex dtk-toggle-split-caps
@kindex C-e d s
@item C-e d s
diff --git a/lisp/dtk-speak.el b/lisp/dtk-speak.el
index e0b34ac49..a0c1f9c93 100644
--- a/lisp/dtk-speak.el
+++ b/lisp/dtk-speak.el
@@ -826,6 +826,16 @@ then set the current local value to the result."
dtk-character-scale
(if prefix "" "locally")))))
+(defun dtk-set-variant (&optional variant)
+ "Set voice variant used by the speech engine to VARIANT.
+
+VARIANT may be any value supported by the speech engine.
+In the case of espeak, it may be a string name or an integer ID.
+
+Omitting VARIANT or setting it to NIL resetss this value to default."
+ (interactive "sVariant name (blank selects default): ")
+ (dtk-interp-set-variant variant (called-interactively-p 'interactive)))
+
(ems-generate-switcher
'dtk-toggle-quiet
'dtk-quiet
@@ -1892,6 +1902,14 @@ Notification is logged in the notifications
buffer unless `dont-log' is T. "
dtk-speaker-process
(format "tts_set_punctuations %s\nd\n" mode)))
+;;}}}
+;;{{{ variant
+(defsubst dtk-interp-set-variant (variant say-it)
+ (cl-declare (special dtk-speaker-process))
+ (process-send-string
+ dtk-speaker-process
+ (format "tts_set_variant %S %S" variant say-it)))
+
;;}}}
;;{{{ reset
diff --git a/lisp/emacspeak-keymap.el b/lisp/emacspeak-keymap.el
index 83a46a086..24eecf9ee 100644
--- a/lisp/emacspeak-keymap.el
+++ b/lisp/emacspeak-keymap.el
@@ -359,6 +359,7 @@
("C-n" dtk-notify-initialize)
("C-o" outloud)
("C-v" global-voice-lock-mode)
+ ("M-v" dtk-set-variant)
("d" dtk-select-server)
("L" dtk-local-server)
("N" dtk-set-next-language)
diff --git a/servers/espeak b/servers/espeak
index 3f19451d7..f20cab5f7 100755
--- a/servers/espeak
+++ b/servers/espeak
@@ -56,6 +56,10 @@ source $wd/tts-lib.tcl
# For example, if there are three available languages:
# langsynth(top)=2
+# langsynth(variant): name (or ID) of the language variant in use
+# If unset, the server defaults to an autoselected male variant
+# Set by the application
+
# voicename: name of the current voice for announcements
# This variable is set by tclespeak
@@ -128,7 +132,7 @@ proc set_previous_lang {say_it} {
set langsynth(current) $index
set langcode(current) $langcode($index)
setLanguage $langsynth(current)
-puts stderr "Language: $langsynth(current) Voice: $voicename"
+ puts stderr "Language: $langsynth(current) Voice: $voicename"
if { [info exists say_it]} {
tts_say "$voicename "
}
@@ -140,15 +144,15 @@ proc set_lang {{name "en"} {say_it "nil"}} {
global langsynth
global langalias
global langcode
-global voicename
- if { ![info exists langalias($name)]} {
- return
- }
+ global voicename
+ if { ![info exists langalias($name)]} {
+ return
+ }
- if { $langalias($name) == $langsynth(current) } {
+ if { $langalias($name) == $langsynth(current) } {
return
- }
-
+ }
+
set langsynth(current) $langalias($name)
set langcode(current) $langcode($langalias($name))
setLanguage $langsynth(current)
@@ -169,6 +173,26 @@ proc set_preferred_lang {alias lang} {
set langalias($alias) $langalias($lang)
}
+# tts_set_variant - clears the variant
+# tts_set_variant "victor" - sets the variant
+# tts_set_variant 1 - sets the variant by index
+proc tts_set_variant {{new_variant ""} {say_it "nil"}} {
+ global langsynth
+ global voicename
+ set new_variant [string trim $new_variant]
+ if { $new_variant == "" || $new_variant == "nil" } {
+ unset langsynth(variant)
+ } else {
+ set langsynth(variant) $new_variant
+ }
+ # Re-set the current voice, having updated the variant
+ setLanguage $langsynth(current)
+ if { $say_it != "nil" } {
+ tts_say "$voicename "
+ }
+
+}
+
#debug
proc list_lang {} {
global langcode
@@ -198,7 +222,7 @@ proc tts_set_punctuations {mode} {
proc tts_set_speech_rate {rate} {
global tts
- set factor $tts(char_factor)
+ set factor $tts(char_factor)
set tts(speech_rate) $rate
setRate 0 $rate
service
diff --git a/servers/native-espeak/tclespeak.cpp
b/servers/native-espeak/tclespeak.cpp
index 4c29f1a98..790daf30d 100644
--- a/servers/native-espeak/tclespeak.cpp
+++ b/servers/native-espeak/tclespeak.cpp
@@ -39,12 +39,13 @@
#include <assert.h>
#include <espeak-ng/speak_lib.h>
+#include <set>
+#include <sstream>
#include <stdlib.h>
#include <string.h>
+#include <string>
#include <sys/time.h>
#include <tcl.h>
-#include <set>
-#include <string>
#include <vector>
using std::set;
using std::string;
@@ -74,7 +75,7 @@ int Synchronize(ClientData, Tcl_Interp *, int, Tcl_Obj
*CONST[]);
int Pause(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST[]);
int Resume(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST[]);
-static void initLanguage(Tcl_Interp *interp);
+static int initLanguage(Tcl_Interp *interp);
static int getLangIndex(Tcl_Interp *interp, unsigned long *theIndex);
//>
@@ -121,8 +122,7 @@ int Tclespeak_Init(Tcl_Interp *interp) {
TclEspeakFree);
//>
- initLanguage(interp);
- return TCL_OK;
+ return initLanguage(interp);
}
int GetRate(ClientData handle, Tcl_Interp *interp, int objc,
@@ -166,17 +166,13 @@ int SetRate(ClientData handle, Tcl_Interp *interp,
int objc,
//>
//<say
-static bool closeTags(string input, string &output) {
- char *tag_orig = (char *)malloc(sizeof(char) * (input.size() + 1));
- strncpy(tag_orig, input.c_str(), input.size());
- output = "";
-
+static bool closeTags(const string input, string &output) {
+ std::ostringstream closingTags;
// check that a text (non whitespace) is present
- char *tag = tag_orig;
int a_tag_count = 0;
bool a_text_is_present = false;
- while (*tag) {
+ for (auto tag = input.cbegin(); tag != input.cend(); ++tag) {
if (*tag == '<') {
a_tag_count++;
}
@@ -188,31 +184,33 @@ static bool closeTags(string input, string &output) {
if ((*tag == '>') && a_tag_count) {
a_tag_count--;
}
- tag++;
}
if (a_text_is_present) {
- tag = tag_orig;
- while (tag) {
+ string::size_type tag_pos = input.size();
+ if (string::npos == tag_pos) {
+ fprintf(stderr, "Synthesizer argument of size (size_t)(-1),
ignoring "
+ "last chraracter\n");
+ --tag_pos;
+ }
+ while (string::npos != tag_pos) {
// look for a '<'
- tag = strrchr(tag_orig, '<');
-
- if (tag) {
- char *end = strchr(tag, ' ');
- if (!end && (NULL == strchr(tag, '/'))) {
- end = strchr(tag, '>');
+ tag_pos = input.find_last_of('<', tag_pos);
+ if (string::npos != tag_pos) {
+ string::size_type end = input.find_first_of(' ', tag_pos);
+ if ((string::npos != end) &&
+ (string::npos == input.find_first_of('/', tag_pos))) {
+ end = input.find_first_of('>', tag_pos);
}
- if (end && (tag + 1 < end)) {
- *end = 0;
- output += "</" + string(tag + 1) + ">";
+ if ((string::npos != end) && (tag_pos + 1 < end)) {
+ closingTags << "</" << input.substr(tag_pos + 1, end) << ">";
}
- *tag = 0;
+ tag_pos--; // Start search before previous tag to avoid
infinite loop
}
}
}
- free(tag_orig);
-
+ output.assign(closingTags.str());
return a_text_is_present;
}
@@ -228,8 +226,14 @@ int Say(ClientData handle, Tcl_Interp *interp, int
objc,
string a_ssml = a_begin_ssml + a_end_ssml;
unsigned int unique_identifier = 0;
- espeak_Synth(a_ssml.c_str(), a_ssml.length() + 1, 0,
POS_CHARACTER, 0,
- espeakCHARS_UTF8 | espeakSSML, &unique_identifier,
NULL);
+ if (EE_OK != espeak_Synth(a_ssml.c_str(), a_ssml.length() + 1, 0,
+ POS_CHARACTER, 0,
+ espeakCHARS_UTF8 | espeakSSML,
+ &unique_identifier, NULL)) {
+ Tcl_AppendResult(
+ interp, "Could not synthesize string: ", a_ssml.c_str(),
NULL);
+ return TCL_ERROR;
+ }
}
}
}
@@ -363,17 +367,37 @@ int getTTSVersion(ClientData handle, Tcl_Interp
*interp, int objc,
static vector<string> available_languages;
-static void SetLanguageHelper(Tcl_Interp *interp, size_t aIndex) {
- espeak_VOICE *current_voice = NULL;
- espeak_VOICE a_voice;
- memset(&a_voice, 0, sizeof(espeak_VOICE));
- a_voice.languages = (char *)available_languages[aIndex].c_str();
- a_voice.gender = 1;
- espeak_SetVoiceByProperties(&a_voice);
- current_voice = espeak_GetCurrentVoice();
+static int SetLanguageHelper(Tcl_Interp *interp, size_t aIndex) {
+ espeak_ERROR voice_status = espeak_ERROR::EE_OK;
+ Tcl_Obj *variant_name = Tcl_GetVar2Ex(interp, "langsynth", "variant", 0);
+ if (variant_name) {
+ int variant_name_len = 0;
+ char *variant_name_data =
+ Tcl_GetStringFromObj(variant_name, &variant_name_len);
+ string variant_name_str(variant_name_data, variant_name_len);
+ string name = available_languages[aIndex] + "+" + variant_name_str;
+ voice_status = espeak_SetVoiceByName(name.c_str());
+ if (espeak_ERROR::EE_OK != voice_status) {
+ fprintf(stderr,
+ "Could not load voice %s, falling back to language-based
search",
+ name.c_str());
+ }
+ }
+
+ if (!variant_name || espeak_ERROR::EE_OK != voice_status) {
+ espeak_VOICE a_voice;
+ memset(&a_voice, 0, sizeof(espeak_VOICE));
+ a_voice.languages = (char *)available_languages[aIndex].c_str();
+ a_voice.gender = 1;
+ voice_status = espeak_SetVoiceByProperties(&a_voice);
+ }
+ if (espeak_ERROR::EE_OK != voice_status) {
+ Tcl_AppendResult(interp, "could not set voice");
+ return TCL_ERROR;
+ }
+ espeak_VOICE *current_voice = espeak_GetCurrentVoice();
Tcl_SetVar(interp, "voicename", current_voice->name, 0);
- // But what if we couldn't set the voice? Need some better error
handling.
- return;
+ return TCL_OK;
}
int SetLanguage(ClientData eciHandle, Tcl_Interp *interp, int objc,
@@ -381,8 +405,9 @@ int SetLanguage(ClientData eciHandle, Tcl_Interp
*interp, int objc,
unsigned long aIndex = 0;
if (getLangIndex(interp, &aIndex)) {
- SetLanguageHelper(interp, aIndex);
+ return SetLanguageHelper(interp, aIndex);
}
+ // TODO: Error reporting for this
return TCL_OK;
}
@@ -404,7 +429,7 @@ static vector<string> ParseLanguages(const char
*lang_str) {
return voice_langs;
}
-static void initLanguage(Tcl_Interp *interp) {
+static int initLanguage(Tcl_Interp *interp) {
// List the available languages
set<string> unique_languages;
int i = 0;
@@ -471,11 +496,15 @@ static void initLanguage(Tcl_Interp *interp) {
Tcl_SetVar2(interp, "langsynth", "current", buffer, 0);
Tcl_SetVar2(interp, "langcode", "current", "en", 0);
}
- SetLanguageHelper(interp, default_index);
+
+ if (TCL_OK != SetLanguageHelper(interp, default_index)) {
+ return TCL_ERROR;
+ }
// Presumably we have at least one language, namely English,
// so no chance of underflowing size_t with this subtraction:
snprintf(buffer, sizeof(buffer), "%lu", lang_count - 1);
Tcl_SetVar2(interp, "langsynth", "top", buffer, 0);
+ return TCL_OK;
}
static int getLangIndex(Tcl_Interp *interp, unsigned long *theIndex) {
--
2.25.1
1 year, 6 months
Speak carets in LaTeX
by Isaac Leonard
Hello everyone
Does anyone know how to make emacspeak read out carets when reading
LaTeX or know where I should look to find out?
Currently it pauses on it but doesn't say anything unless I specifically
move the pointer to it.
--
Best regards Isaac
1 year, 6 months
Missing command?
by Hwaen Ch'uqi
Greetings,
I am using emacspeak-54.0, and it appears that dtk-notify-shutdown no
longer exists? The dtk-notify-stop command does not fully do what I
wish, and the overlapping of voices is frustrating indeed! Is there
any work-around?
Thanks for any help!
Hwaen Ch'uqi
1 year, 6 months
Crashes on macos on startup
by Isaac Leonard
Hi all, I am new to the mailing list and to mailing lists in general.
I saw that I should introduce myself so here goes
I am a 21 year old programmer from australia, I am totally blind and
have used emacspeak for around 2 years now.
I am currently studying computer science at university and mainly use
emacs for all of my writing when possible.
I've recently had an issue with emacspeak crashing when I start emacs.
A few months ago when version 53 was released I tried to upgrade however
it caused emacs to crash on startup around 50% of the time.
I reverted to version 52.0 until the other day when I decided to try
upgrade again to version 54.0 and just deal with the crashes.
Yesterday however I attempted to open emacs 4 times in a row with
it crashing each time so decided to follow it up further.
This only happens with emacspeak53.0 or 54.0, previous versions have
never crashed before.
Here is the errors printed to the terminal from when it crashed those 4 times :
$emacs
Emacs-x86_64-10_14(2360,0x700009d2e000) malloc: *** error for object
0x70000a5307e8: pointer being f
reed was not allocated
Fatal error 11: Segmentation fault
Abort trap: 6
$emacs
Emacs-x86_64-10_14(2433,0x700003241000) malloc: *** error for object
0x7ffee6db5200: pointer being f
reed was not allocated
Emacs-x86_64-10_14(2433,0x10a8415c0) malloc: *** error for object
0x7ffee6db5310: pointer being free
d was not allocated
Emacs-x86_64-10_14(2433,0x700003241000) malloc: *** set a breakpoint
in malloc_error_break to debug
Emacs-x86_64-10_14(2433,0x10a8415c0) malloc: *** set a breakpoint in
malloc_error_break to debug
Fatal error 6: Abort trap
Abort trap: 6
$emacs
Emacs-x86_64-10_14(2497,0x70000ce9d000) malloc: *** error for object
0x7ffee91b45a0: pointer being f
reed was not allocated
Emacs-x86_64-10_14(2497,0x70000ce9d000) malloc: *** set a breakpoint
in malloc_error_break to debug
Fatal error 4: Illegal instruction
Abort trap: 6
$emacs
Emacs-x86_64-10_14(2576,0x700002fcd000) malloc: *** error for object
0x7ffee07fcac8: pointer being f
reed was not allocated
Emacs-x86_64-10_14(2576,0x700002fcd000) malloc: *** set a breakpoint
in malloc_error_break to debug
Fatal error 4: Illegal instruction
Abort trap: 6
Do you know if there is anything I can do to fix this or is it a bug in
emacspeak?
It seems to me as though it is attempting to access a freed object.
Has anyone else had this issue?
--
Best regards Isaac
1 year, 7 months