I've been making a bunch of modifications to parts of Emacspeak to get speech output that I like better; I figured others might also find them useful so I'm attaching them here as 3 separate patches. All the features are behind customization variables, so you can control them through the usual customize interface, and they default to what Emacspeak does already. Here's a summary of each patch and the customizations each adds: ====== * pitch.patch: This has straightforward Espeak-specific settings for default pitch and pitch-range: * espeak-default-speech-pitch: (integer, defaults to 50) * espeak-default-speech-pitch-range: (integer, defaults to 50) ------ * move_line_ends.patch: This lets you change what gets spoken when moving to the beginning or end of a line. * emacspeak-speak-at-ends-of-line: (can be line, word, character, or nothing; defaults to line) ------ * speak_indent.patch: This lets you customize precisely how line indentation is spoken. The settings fall into 3 groups. * emacspeak-audio-indentation-tab-grouped: (toggle, defaults to off) When this mode is turned on, indentation levels are split up into two components: a tab component which is multiple of the tab-width, and a space offset. For example, if your tab-width is set to 4, an indentation of 11 will get split up into a multiple of 2 and an offset of 3. The text for the indentation level announcements is stitched together by inserting the digits between string segments which are given in the following settings. * settings for normal indentation announcements: * emacspeak-audio-indentation-before: defaults to "indent " * emacspeak-audio-indentation-after: defaults to "" * settings for tab-grouped indentation announcements: * emacspeak-audio-indentation-before-tab: (defaults to "") * emacspeak-audio-indentation-after-tab: (defaults to " tabs") * emacspeak-audio-indentation-tab-grouped-separator: (defaults to " ") * emacspeak-audio-indentation-before-offset: (defaults to "") * emacspeak-audio-indentation-after-offset: (defaults to " spaces") To illustrate how this works: if you again have tab-width set to 4 and an indentation level of 11, with the following settings: emacspeak-audio-indentation-before = "indent " emacspeak-audio-indentation-after = "" emacspeak-audio-indentation-before-tab = "indent " emacspeak-audio-indentation-after-tab = " tabs" emacspeak-audio-indentation-tab-grouped-separator = ", and " emacspeak-audio-indentation-before-offset = "" emacspeak-audio-indentation-after-offset = " spaces" In normal mode, this gets announced as "indent 11", and in tab-grouped mode, this gets announced as "indent 2 tabs, and 3 spaces". ------ Here's the relevant part of my .emacs where I've used these customizations: (custom-set-variables ;; custom-set-variables was added by Custom. ;; If you edit it by hand, you could mess it up, so be careful. ;; Your init file should contain only one such instance. ;; If there is more than one, they won't work right. '(emacspeak-audio-indentation-after " spaces") '(emacspeak-audio-indentation-before "") '(emacspeak-character-echo nil) '(emacspeak-speak-at-ends-of-line (quote char)) '(espeak-default-speech-pitch 75) '(espeak-default-speech-pitch-range 100) '(espeak-default-speech-rate 900) '(package-selected-packages (quote (evil))) '(tab-width 4)) ====== To build emacspeak with any of these patches: (1) Clone the Emacspeak source from Github. (2) Download the patchfile and move it into the emacspeak directory. (3) In the emacspeak directory, apply the patch by running the command: git apply <patch_file> (4) Continue building Emacspeak as usual. You can also check these modifications out of branches with corresponding names from this Git repository: https://www.github.com/hussainmkj/emacspeak.git/. Note that if you want to do a git-based build with more than one of the branches, you should check out master and then do a single-merge of the branches you want: git checkout master git merge --no-commit <branch_1> <branch_2> [<branch_3>] Trying to merge them in sequentially will result in merge conflicts. ------ If anyone does find these useful, or wants more more customization options for the features, let me know! Cheers, Hussain
From 5b5bdc607639d3b0bf2db4486ed11474d625cd30 Mon Sep 17 00:00:00 2001 From: Hussain Jasim <hussain.jasim@xxxxxxxxxxx> Date: Fri, 8 Jun 2018 10:53:52 -0400 Subject: [PATCH 1/3] Added functions to the Espeak server for setting pitch and pitch-range. --- servers/espeak | 20 +++++++++ servers/native-espeak/tclespeak.cpp | 88 +++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) diff --git a/servers/espeak b/servers/espeak index cdc68c487..f4db85ced 100755 --- a/servers/espeak +++ b/servers/espeak @@ -206,6 +206,26 @@ proc tts_set_speech_rate {rate} { return "" } +proc tts_set_speech_pitch {pitch} { + global tts + + set factor $tts(char_factor) + set tts(speech_pitch) $pitch + setPitch 0 $pitch + service + return "" +} + +proc tts_set_speech_pitch_range {pitch_range} { + global tts + + set factor $tts(char_factor) + set tts(speech_pitch_range) $pitch_range + setPitchRange 0 $pitch_range + service + return "" +} + proc tts_set_character_scale {factor} { global tts diff --git a/servers/native-espeak/tclespeak.cpp b/servers/native-espeak/tclespeak.cpp index c7e64a15d..48143de38 100644 --- a/servers/native-espeak/tclespeak.cpp +++ b/servers/native-espeak/tclespeak.cpp @@ -68,6 +68,10 @@ extern "C" EXPORT int Tclespeak_Init(Tcl_Interp *interp); int SetRate(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST[]); int GetRate(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST[]); +int SetPitch(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST[]); +int GetPitch(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST[]); +int SetPitchRange(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST[]); +int GetPitchRange(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST[]); int getTTSVersion(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST[]); int Punct(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST[]); int Caps(ClientData, Tcl_Interp *, int, Tcl_Obj *CONST[]); @@ -113,6 +117,14 @@ int Tclespeak_Init(Tcl_Interp *interp) { TclEspeakFree); Tcl_CreateObjCommand(interp, "getRate", GetRate, (ClientData)handle, TclEspeakFree); + Tcl_CreateObjCommand(interp, "setPitch", SetPitch, (ClientData)handle, + TclEspeakFree); + Tcl_CreateObjCommand(interp, "getPitch", GetPitch, (ClientData)handle, + TclEspeakFree); + Tcl_CreateObjCommand(interp, "setPitchRange", SetPitchRange, (ClientData)handle, + TclEspeakFree); + Tcl_CreateObjCommand(interp, "getPitchRange", GetPitchRange, (ClientData)handle, + TclEspeakFree); Tcl_CreateObjCommand(interp, "ttsVersion", getTTSVersion, (ClientData)handle, TclEspeakFree); Tcl_CreateObjCommand(interp, "punct", Punct, (ClientData)handle, NULL); @@ -182,6 +194,82 @@ int SetRate(ClientData handle, Tcl_Interp *interp, int objc, return success ? TCL_OK : TCL_ERROR; } +int GetPitch(ClientData handle, Tcl_Interp *interp, int objc, + Tcl_Obj *CONST objv[]) { + int rc, pitch, voice; + if (objc != 2) { + Tcl_AppendResult(interp, "Usage: getPitch voiceCode ", TCL_STATIC); + return TCL_ERROR; + } + rc = Tcl_GetIntFromObj(interp, objv[1], &voice); + if (rc != TCL_OK) return rc; + + pitch = espeak_GetParameter(espeakPITCH, 1); + + Tcl_SetObjResult(interp, Tcl_NewIntObj(pitch)); + return TCL_OK; +} + +int SetPitch(ClientData handle, Tcl_Interp *interp, int objc, + Tcl_Obj *CONST objv[]) { + static int current_pitch = -1; + int rc, pitch, voice; + int success = 1; + if (objc != 3) { + Tcl_AppendResult(interp, "Usage: setPitch voiceCode speechPitch ", + TCL_STATIC); + return TCL_ERROR; + } + rc = Tcl_GetIntFromObj(interp, objv[1], &voice); + if (rc != TCL_OK) return rc; + rc = Tcl_GetIntFromObj(interp, objv[2], &pitch); + if (rc != TCL_OK) return rc; + + if (pitch != current_pitch) { + success = (espeak_SetParameter(espeakPITCH, pitch, 0) == EE_OK); + if (success) current_pitch = pitch; + } + return success ? TCL_OK : TCL_ERROR; +} + +int GetPitchRange(ClientData handle, Tcl_Interp *interp, int objc, + Tcl_Obj *CONST objv[]) { + int rc, pitch_range, voice; + if (objc != 2) { + Tcl_AppendResult(interp, "Usage: getPitchRange voiceCode ", TCL_STATIC); + return TCL_ERROR; + } + rc = Tcl_GetIntFromObj(interp, objv[1], &voice); + if (rc != TCL_OK) return rc; + + pitch_range = espeak_GetParameter(espeakRANGE, 1); + + Tcl_SetObjResult(interp, Tcl_NewIntObj(pitch_range)); + return TCL_OK; +} + +int SetPitchRange(ClientData handle, Tcl_Interp *interp, int objc, + Tcl_Obj *CONST objv[]) { + static int current_pitch_range = -1; + int rc, pitch_range, voice; + int success = 1; + if (objc != 3) { + Tcl_AppendResult(interp, "Usage: setPitchRange voiceCode speechPitchRange ", + TCL_STATIC); + return TCL_ERROR; + } + rc = Tcl_GetIntFromObj(interp, objv[1], &voice); + if (rc != TCL_OK) return rc; + rc = Tcl_GetIntFromObj(interp, objv[2], &pitch_range); + if (rc != TCL_OK) return rc; + + if (pitch_range != current_pitch_range) { + success = (espeak_SetParameter(espeakRANGE, pitch_range, 0) == EE_OK); + if (success) current_pitch_range = pitch_range; + } + return success ? TCL_OK : TCL_ERROR; +} + //> //<say -- 2.15.0 From 7f4a4e2246f8918b03e4766652bc613371fa0fd4 Mon Sep 17 00:00:00 2001 From: Hussain Jasim <hussain.jasim@xxxxxxxxxxx> Date: Wed, 20 Jun 2018 22:27:50 -0400 Subject: [PATCH 2/3] Added functions to the Espeak client for interactively setting pitch and pitch-range. --- lisp/espeak-voices.el | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/lisp/espeak-voices.el b/lisp/espeak-voices.el index e98a7e4a7..59c21c648 100644 --- a/lisp/espeak-voices.el +++ b/lisp/espeak-voices.el @@ -57,6 +57,82 @@ (string-match "espeak$" (getenv "DTK_PROGRAM"))) (setq-default dt-speech-rate val)))) +;;{{{ espeak-specific controls +;; Functions to interface with the Espeak server and interactively set pitch and pitch-range, +;; which are defined here instead of dtk-speech.el because they are exclusive to Espeak. + +;;{{{ espeak pitch +(defun espeak-interp-set-pitch (pitch) + (cl-declare (special dtk-speaker-process)) + (process-send-string dtk-speaker-process + (format "tts_set_speech_pitch %s\n" + pitch))) + +;;;###autoload +(defun espeak-set-pitch (pitch &optional prefix) + "Set speaking PITCH for espeak. +Interactive PREFIX arg means set the global default value, and then set the +current local value to the result." + (interactive + (list + (read-from-minibuffer "Enter new pitch: ") + current-prefix-arg)) + (cl-declare (special espeak-speech-pitch dtk-speaker-process + espeak-default-speech-pitch + dtk-program dtk-speak-server-initialized)) + (when dtk-speak-server-initialized + (cond + (prefix + (unless (eq dtk-speaker-process (dtk-notify-process)) + (let ((dtk-speaker-process (dtk-notify-process))) + (espeak-set-pitch pitch))) + (setq espeak-default-speech-pitch pitch) + (setq-default espeak-speech-pitch pitch) + (setq espeak-speech-pitch pitch)) + (t (setq espeak-speech-pitch pitch))) + (espeak-interp-set-pitch pitch) + (when (called-interactively-p 'interactive) + (message "Set speech pitch to %s %s" + pitch + (if prefix "" "locally"))))) + +;;}}} +;;{{{ espeak pitch-range +(defun espeak-interp-set-pitch-range (pitch-range) + (cl-declare (special dtk-speaker-process)) + (process-send-string dtk-speaker-process + (format "tts_set_speech_pitch_range %s\n" + pitch-range))) + +;;;###autoload +(defun espeak-set-pitch-range (pitch-range &optional prefix) + "Set speaking PITCH for espeak. +Interactive PREFIX arg means set the global default value, and then set the +current local value to the result." + (interactive + (list + (read-from-minibuffer "Enter new pitch-range: ") + current-prefix-arg)) + (cl-declare (special espeak-speech-pitch-range dtk-speaker-process + espeak-default-speech-pitch-range + dtk-program dtk-speak-server-initialized)) + (when dtk-speak-server-initialized + (cond + (prefix + (unless (eq dtk-speaker-process (dtk-notify-process)) + (let ((dtk-speaker-process (dtk-notify-process))) + (espeak-set-pitch-range pitch-range))) + (setq espeak-default-speech-pitch-range pitch-range) + (setq-default espeak-speech-pitch-range pitch-range) + (setq espeak-speech-pitch-range pitch-range)) + (t (setq espeak-speech-pitch-range pitch-range))) + (espeak-interp-set-pitch-range pitch-range) + (when (called-interactively-p 'interactive) + (message "Set speech pitch-range to %s %s" + pitch-range + (if prefix "" "locally"))))) + +;;}}} ;;}}} ;;{{{ Top-Level TTS Call -- 2.15.0 From 9174baeefde2f8ff561de1ce485832d3da526c12 Mon Sep 17 00:00:00 2001 From: Hussain Jasim <hussain.jasim@xxxxxxxxxxx> Date: Wed, 20 Jun 2018 22:33:34 -0400 Subject: [PATCH 3/3] Added defaults and customizations to the Espeak client for pitch and pitch-range. --- lisp/espeak-voices.el | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/lisp/espeak-voices.el b/lisp/espeak-voices.el index 59c21c648..a84435d41 100644 --- a/lisp/espeak-voices.el +++ b/lisp/espeak-voices.el @@ -57,6 +57,27 @@ (string-match "espeak$" (getenv "DTK_PROGRAM"))) (setq-default dt-speech-rate val)))) +(defcustom espeak-default-speech-pitch 50 + "Default speech pitch for eSpeak." + :group 'tts + :type 'integer + :set #'(lambda(sym val) + (set-default sym val) + (when (and (getenv "DTK_PROGRAM") + (string-match "espeak$" (getenv "DTK_PROGRAM"))) + (setq-default espeak-speech-pitch val)))) + +(defcustom espeak-default-speech-pitch-range 50 + "Default speech pitch range for eSpeak." + :group 'tts + :type 'integer + :set #'(lambda(sym val) + (set-default sym val) + (when (and (getenv "DTK_PROGRAM") + (string-match "espeak$" (getenv "DTK_PROGRAM"))) + (setq-default espeak-speech-pitch-range val)))) + +;;}}} ;;{{{ espeak-specific controls ;; Functions to interface with the Espeak server and interactively set pitch and pitch-range, ;; which are defined here instead of dtk-speech.el because they are exclusive to Espeak. @@ -561,6 +582,8 @@ and TABLE gives the values along that dimension." "Configure TTS environment to use eSpeak." (cl-declare (special tts-default-speech-rate espeak-default-speech-rate + espeak-default-speech-pitch + espeak-default-speech-pitch-range dtk-speaker-process emacspeak-unspeakable-rule)) (fset 'tts-list-voices'espeak-list-voices) @@ -571,6 +594,8 @@ and TABLE gives the values along that dimension." (setq tts-default-voice nil) (setq tts-default-speech-rate espeak-default-speech-rate) (set-default 'tts-default-speech-rate espeak-default-speech-rate) + (espeak-set-pitch espeak-default-speech-pitch t) + (espeak-set-pitch-range espeak-default-speech-pitch-range t) (espeak-setup-character-to-speech-table) (dtk-unicode-update-untouched-charsets '(ascii latin-iso8859-1))) @@ -579,10 +604,12 @@ and TABLE gives the values along that dimension." ;;;###autoload (defun espeak-make-tts-env () "Constructs a TTS environment for Espeak." - (cl-declare (special espeak-default-speech-rate)) + (cl-declare (special espeak-default-speech-rate espeak-default-speech-pitch espeak-default-speech-pitch-range)) (make-tts-env :name :espeak :default-voice 'paul :default-speech-rate espeak-default-speech-rate + :default-speech-pitch espeak-default-speech-pitch + :default-speech-pitch-range espeak-default-speech-pitch-range :list-voices #'espeak-list-voices :acss-voice-defined-p #'espeak-voice-defined-p :get-acss-voice-command #'espeak-get-voice-command -- 2.15.0
From 666f5664ba9a58fd6d6cbc19b9459ffb061bc5e6 Mon Sep 17 00:00:00 2001 From: Hussain Jasim <hussain.jasim@xxxxxxxxxxx> Date: Wed, 20 Feb 2019 00:09:24 -0500 Subject: [PATCH] Implemented a customization variable for the unit of text that should be spoken when moving to the beginning and end of a line, and pulled this behaviour out into its own advice. --- lisp/emacspeak-advice.el | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/lisp/emacspeak-advice.el b/lisp/emacspeak-advice.el index baa6abfbc..7f5b2a982 100644 --- a/lisp/emacspeak-advice.el +++ b/lisp/emacspeak-advice.el @@ -2247,9 +2247,7 @@ Produce an auditory icon if possible." (cl-loop for f in - '(beginning-of-line end-of-line - move-beginning-of-line move-end-of-line - recenter-top-bottom recenter) + '(recenter-top-bottom recenter) do (eval `(defadvice ,f (before emacspeak pre act comp) @@ -2259,6 +2257,34 @@ Produce an auditory icon if possible." (emacspeak-auditory-icon 'select-object))))) ;;}}} +;;{{{ speak when moving to beginning and end of line + +(defcustom emacspeak-speak-at-ends-of-line 'line + "Current text unit under point to speak when moving to the beginning or end of a line. Default is `line'." + :group 'emacspeak-advice + :type '(choice + (const :tag "nothing" nil) + (const :tag "line" line) + (const :tag "word" word) + (const :tag "character" char))) + +(cl-loop + for f in + '(beginning-of-line end-of-line + move-beginning-of-line move-end-of-line) + do + (eval + `(defadvice ,f (after emacspeak pre act comp) + "Speak `emacspeak-speak-at-ends-of-line' under point." + (when (and (ems-interactive-p) (not (eq emacspeak-speak-at-ends-of-line 'nil))) + (and dtk-stop-immediately (dtk-stop)) + (cond + ((eq emacspeak-speak-at-ends-of-line 'line) (emacspeak-speak-line)) + ((eq emacspeak-speak-at-ends-of-line 'word) (emacspeak-speak-word)) + ((eq emacspeak-speak-at-ends-of-line 'char) (emacspeak-speak-char t))) + (emacspeak-auditory-icon 'select-object))))) + +;;}}} ;;{{{ yanking and popping (cl-loop -- 2.15.0
From 932adfe6dc83fed1bad65dc529bfa43e64de3744 Mon Sep 17 00:00:00 2001 From: Hussain Jasim <hussain.jasim@xxxxxxxxxxx> Date: Wed, 20 Feb 2019 01:43:05 -0500 Subject: [PATCH] Implemented customization variables for: (1) controling the phrasing of line indentation level announcements; (2) grouping indentation level by multiples and offsets of the tab-width. --- lisp/emacspeak-speak.el | 54 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/lisp/emacspeak-speak.el b/lisp/emacspeak-speak.el index e61448b03..90a04305a 100644 --- a/lisp/emacspeak-speak.el +++ b/lisp/emacspeak-speak.el @@ -682,6 +682,46 @@ results in the Dectalk producing a tone whose length is a function of the line's indentation. Specifying `speak' results in the number of initial spaces being spoken.") +(defcustom emacspeak-audio-indentation-before "indent " + "Option specifying the phrase that is spoken before the indent level, when speaking line indentation." + :group 'emacspeak-speak + :type 'string) + +(defcustom emacspeak-audio-indentation-after "" + "Option specifying the phrase that is spoken after the indent level, when speaking line indentation." + :group 'emacspeak-speak + :type 'string) + +(defcustom emacspeak-audio-indentation-tab-grouped nil + "Option indicating whether indentation levels should be grouped into a multiple of the tab-width and an offset, when speaking line indentation." + :group 'emacspeak-speak + :type 'boolean) + +(defcustom emacspeak-audio-indentation-before-tab "" + "Option specifying the phrase that is spoken before the tab-width multiple, when speaking tab-grouped line indentation." + :group 'emacspeak-speak + :type 'string) + +(defcustom emacspeak-audio-indentation-after-tab " tabs" + "Option specifying the phrase that is spoken after the tab-width multiple, when speaking tab-grouped line indentation." + :group 'emacspeak-speak + :type 'string) + +(defcustom emacspeak-audio-indentation-tab-grouped-separator " " + "Option specifying the phrase that is spoken between the tab-width multiple and the tab-width offset, when speaking tab-grouped line indentation." + :group 'emacspeak-speak + :type 'string) + +(defcustom emacspeak-audio-indentation-before-offset "" + "Option specifying the phrase that is spoken before the tab-width offset, when speaking tab-grouped line indentation." + :group 'emacspeak-speak + :type 'string) + +(defcustom emacspeak-audio-indentation-after-offset " spaces" + "Option specifying the phrase that is spoken after the tab-width offset, when speaking tab-grouped line indentation." + :group 'emacspeak-speak + :type 'string) + ;;}}} ;;{{{ filtering columns @@ -1017,7 +1057,19 @@ with auditory icon `more'. These can then be spoken using command (when (and (null arg) indent (> indent 0) (eq 'speak emacspeak-audio-indentation-method)) - (setq indent (format "indent %d" indent)) + (setq indent + (cond + ((not emacspeak-audio-indentation-tab-grouped) + (format "%s%d%s" emacspeak-audio-indentation-before indent emacspeak-audio-indentation-after)) + ((< indent tab-width) + (format "%s%d%s" emacspeak-audio-indentation-before-offset indent emacspeak-audio-indentation-after-offset)) + ((eq 0 (% indent tab-width)) + (format "%s%d%s" emacspeak-audio-indentation-before-tab (/ indent tab-width) emacspeak-audio-indentation-after-tab)) + ( + (format "%s%d%s%s%s%d%s" + emacspeak-audio-indentation-before-tab (/ indent tab-width) emacspeak-audio-indentation-after-tab + emacspeak-audio-indentation-tab-grouped-separator + emacspeak-audio-indentation-before-offset (% indent tab-width) emacspeak-audio-indentation-after-offset)))) (setq indent (propertize indent 'personality voice-indent)) (setq line (concat indent line))) (when linenum -- 2.15.0
|May 1995 - Last Year |Current Year|
If you have questions about this archive or had problems using it, please contact us.