Updated speechd-el config
Unfortunately being again forced to use an audio desktop, following my updated speechd-el specific emacs configuartion:
prepare for speechd-el
- Being used to Common Lisp - I want lexical-binding t.
- I want to have a backtrace on error that happens. That makes it easier grok an error.
- Whenever a Help buffer opens up, I want to switch to the window.
- Add the directory of speechd-el to the info-directories list.
- I want org files startup folded
;;; ... -*- lexical-binding: t -*- (setq debug-on-error t) (setf help-window-select t) (setf Info-additional-directory-list (list "/home/okflo/install/speechd-el/")) (setq org-startup-folded t)
load speechd-el
- add the location of speechd-el to load-path
- reduce to ssip output driver, as I don't use braille currently.
- require speechd, speechd-ssip, speechd-speak and speechd-el
(add-to-list 'load-path (expand-file-name "~/install/speechd-el/")) (require 'speechd) (require 'speechd-ssip) (require 'speechd-speak) (require 'speechd-el) (setf speechd-out-active-drivers '(ssip))
Settings
various
Various speechd-el specific settings. I prefer explicitly setting variables over customize:
(setf speechd-speak-read-command-keys nil) (setf speechd-speak-read-command-name nil) (setf speechd-speak-whole-line t) (setf speechd-speak-echo 'character) (setf speechd-speak-use-index-marks t) (setf speechd-speak-buffer-insertions 'one-line) (setf speechd-speak-ignore-command-keys '( forward-char backward-char right-char left-char next-line previous-line delete-char comint-delchar-or-maybe-eof delete-backward-char backward-delete-char-untabify delete-forward-char c-electric-backspace c-electric-delete-forward)) (setf speechd-speak-by-properties-on-movement t) (setf speechd-speak-state-changes '(buffer-identification ;; buffer-read-only frame-name ;; frame-identification major-mode ;; minor-modes buffer-file-coding ;; terminal-coding ;; input-method process))
faces
Define voices and for which face to use which voice, f.e.:
- higher pitch for various links
- lower pitch for headlines or directories
- reduce rate for info-quote
(setf speechd-voices '((voice-link . ((pitch . 50))) (voice-function-name . ((pitch . -30) (rate . -10) (style . 3) (punctuation-mode . all) (capital-character-mode . icon))) (voice-whisper . ((pitch . 50) (rate . 10) (style . 3) (punctuation-mode . all) (capital-character-mode . icon))) (voice-distinct . ((pitch . 5) (rate . -40) (style . 3) (punctuation-mode . all) (capital-character-mode . icon))) (voice-heading . ((pitch . -250))) (voice-source-code . ((pitch . 0) (rate . -20) (punctuation-mode . all) (capital-character-mode . icon))))) (setf speechd-face-voices '((font-lock-function-name-face . voice-function-name) (Info-quoted . voice-distinct) (info-title-1 . voice-heading) (info-title-2 . voice-heading) (info-title-3 . voice-heading) (info-title-4 . voice-heading) (info-menu-header . voice-heading) (info-xref . voice-link) (info-xref-visited . voice-link) (link . voice-link) (shr-link . voice-link) (shr-selected-link . voice-link) (eshell-ls-directory . voice-link) (eshell-ls-backup . voice-whisper) (eshell-ls-executable . voice-function-name) (eshell-prompt . voice-heading) (dired-directory . voice-link) (elpher-gemini . voice-link) (org-level-1 . voice-heading) (org-level-2 . voice-heading) (org-level-3 . voice-heading) (org-block . voice-source-code) (org-source . voice-source-code) (org-link . voice-link) (shr-h1 . voice-heading) (shrface-h1-face . voice-heading) (shrface-h2-face . voice-heading) (shrface-h3-face . voice-heading) (shrface-h4-face . voice-heading) (elpher-gemini-heading1 . voice-heading) (elpher-gemini-heading2 . voice-heading) (elpher-gemini-heading3 . voice-heading) (shrface-href-face . voice-link)))
Application specific adaptions
reading and information of window/frame/etc
- 'C-e f': speak associated file of current buffer
- 'C-e TAB w': speak current window size and buffer name
- 'C-e TAB W': speak number of windows in frame and their buffer-names
- 'C-e TAB (': speak number of chars before point in line
- 'C-e TAB )': speak number of chars after point in line
(define-key speechd-speak-mode-map "h" 'forward-paragraph) (defun okflo-speak-file-associated-with-buffer () (interactive) (if buffer-file-name (message buffer-file-name) (message "none"))) (define-key speechd-speak-mode-map "f" 'okflo-speak-file-associated-with-buffer) (defun okflo-speak-window-size () (interactive) (message (format "Window width %s columns and window height %s lines containing %s" (window-total-width) (window-total-height) (buffer-name)))) (define-key speechd-speak-info-map "w" 'okflo-speak-window-size) (defun okflo-describe-windows-in-frame () (interactive) (let* ((windows (window-list)) (buffer-names (cl-loop for iw in windows collect (buffer-name (window-buffer iw))))) (message (format "Frame has %s windows containing buffers %s" (length windows) buffer-names)))) (define-key speechd-speak-info-map "W" 'okflo-describe-windows-in-frame) (defun okflo-position-in-text-line () (interactive) (message (format "Cursor in line at %s" (- (point) (line-beginning-position))))) (define-key speechd-speak-info-map "(" 'okflo-position-in-text-line) (defun okflo-remaining-chars-in-line () (interactive) (message (format "%s remaining chars in line" (- (line-end-position) (point))))) (define-key speechd-speak-info-map ")" 'okflo-remaining-chars-in-line)
minibuffer
Forget about helm, consult and all the other frameworks for file-completions, etc. They won't work together with speechd-el, and actually, vanilla completion buffer is perfect.
- additionally to M-up and M-down also define C-<up>/<left> and C-<down>/<right> for completion, as they are much more reachable on my keyboard.
- add 'M-g M-c' to global keymap - so that we also can jump to
Completions after complete-at-point
- have fore each completion candidate a seperate line in completion buffer.
(define-key minibuffer-mode-map (kbd "C-<up>") 'minibuffer-previous-completion) (define-key minibuffer-mode-map (kbd "C-<left>") 'minibuffer-previous-completion) (define-key minibuffer-mode-map (kbd "C-<down>") 'minibuffer-next-completion) (define-key minibuffer-mode-map (kbd "C-<right>") 'minibuffer-next-completion) (keymap-global-set "M-g M-c" 'switch-to-completions) (setf completions-format 'one-column) (setf completions-detailed t)
Kill-Ring
- <C-e k> to speak next yank
- give feedback for kill-ring-save, how many characters have been saved.
(defun okflo-speak-next-yank () (interactive) (speechd-say-text (car kill-ring))) (define-key speechd-speak-mode-map "k" 'okflo-speak-next-yank) (speechd-speak-function-feedback kill-ring-save after (speechd-say-text (format "Saved %s chars" (length (car kill-ring)))))
eldoc
- eldoc-mode is to noisy, speaking all the time the echo-area
- instead - bind <M-g M-d> to open up a buffer with eldoc information and switch to this buffer.
(global-eldoc-mode -1) (defun okflo-speak-eldoc () (interactive) (eldoc-print-current-symbol-info t) (other-window 1)) (keymap-global-set "M-g M-d" 'okflo-speak-eldoc)
fast switch between german and english voice
my native tongue is (Austrian) German - but I also want to have English.
- <C-e g> switches to German
- <C-e C-g> switches to English
(defun okflo-switch-to-german () (interactive) (speechd-set-voice "petra-ml-embedded-high") (speechd-set-language "de") (speechd-set-rate 28) (message "Deutsch")) (define-key speechd-speak-mode-map "g" 'okflo-switch-to-german) (defun okflo-switch-to-english () (interactive) (speechd-set-voice "allison-embedded-high") (speechd-set-language "en") (speechd-set-rate 14) (message "English")) (define-key speechd-speak-mode-map (kbd "C-g") 'okflo-switch-to-english)
control sound volume
Control volume via amixer
- <C-e -> decreases by 5%
- <C-e +> increases by 5%
(defun okflo-volume- () (interactive) (shell-command "amixer sset Master 5%-") (message "Volume down")) (define-key speechd-speak-mode-map "-" 'okflo-volume-) (defun okflo-volume+ () (interactive) (shell-command "amixer sset Master 5%+") (message "Volume up")) (define-key speechd-speak-mode-map "+" 'okflo-volume+)
filter current buffer in a new buffer
takes the content of the current buffer and copies it into a new buffer "Filter: <buffer-name>". This buffer is associated with no file. Purpose of this buffer is to (destructively) filter the content and make it easier to grok it.
- press 'C-e f' to activate it.
- commands as minor mode `okflo-filter-mode` (always affecting the
whole buffer!) for the new buffer:
- 'C-c d': Delete all lines containing regex.
- 'C-c k': Keep all lines containing regex.
- 'C-c h': Count number of lines containing regex.
(defun okflo-keep-lines (regexp &optional rstart rend interactive) (interactive (progn (barf-if-buffer-read-only) (keep-lines-read-args "Keep lines containing match for regexp"))) (let ((orig-lines (count-lines (point-min) (point-max)))) (keep-lines regexp (point-min) (point-max)) (message (format "Reduced from %s to %s lines" orig-lines (count-lines (point-min) (point-max)))))) (defun okflo-delete-lines (regexp &optional rstart rend interactive) (interactive (progn (barf-if-buffer-read-only) (keep-lines-read-args "Flush lines containing match for regexp"))) (let ((orig-lines (count-lines (point-min) (point-max)))) (flush-lines regexp (point-min) (point-max)) (message (format "Reduced from %s to %s lines" orig-lines (count-lines (point-min) (point-max)))))) (define-minor-mode okflo-filter-mode "Easily descructively parse the content." :lighter "OFM" :keymap `((,(kbd "C-c k") . okflo-keep-lines) (,(kbd "C-c d") . okflo-delete-lines) (,(kbd "C-c h") . how-many))) (defun okflo-filter-buffer () (interactive) (let* ((tobe-filtered-buf (current-buffer)) (buf-name (format "*Filter:<%s>*"(buffer-name tobe-filtered-buf))) (new-buf (get-buffer-create buf-name))) (save-excursion (copy-to-buffer new-buf (point-min) (point-max))) (set-buffer new-buf) (switch-to-buffer new-buf) (okflo-filter-mode))) (define-key speechd-speak-mode-map "F" 'okflo-filter-buffer)
speak time
'C-e t': speaks current time and date - give the string to be spoken a face, to speak it distinctively.
(defun okflo-speak-time () (interactive) (let* ((dt (decode-time (current-time))) (months '(January Febuary March April May June July August September October December)) (time-text (format "Time %s:%s / Date %s %s %s" (decoded-time-hour dt) (decoded-time-minute dt) (nth (1- (decoded-time-month dt)) months) (decoded-time-day dt) (decoded-time-year dt)))) (speechd-say-text (propertize time-text 'face 'mode-line) :priority 'important))) (define-key speechd-speak-mode-map "t" 'okflo-speak-time)
battery
<C-e ~> speaks current battery status of my laptop.
(require 'battery) (defun okflo-speak-battery-status () (interactive) (battery)) (define-key speechd-speak-mode-map "~" 'okflo-speak-battery-status)
eat
- add 'C-e …' to be available in semi-char-mode so that we can read/speak.
(setf eat-semi-char-non-bound-keys '([?\C-e] [?\C-x] [?\C-\\] [?\C-q] [?\C-g] [?\C-h] [?\e ?\C-c] [?\C-u] [?\e ?x] [?\e ?:] [?\e ?!] [?\e ?&] [C-insert] [M-insert] [S-insert] [C-M-insert] [C-S-insert] [M-S-insert] [C-M-S-insert] [C-delete] [M-delete] [S-delete] [C-M-delete] [C-S-delete] [M-S-delete] [C-M-S-delete] [C-deletechar] [M-deletechar] [S-deletechar] [C-M-deletechar] [C-S-deletechar] [M-S-deletechar] [C-M-S-deletechar] [C-up] [C-down] [C-right] [C-left] [M-up] [M-down] [M-right] [M-left] [S-up] [S-down] [S-right] [S-left] [C-M-up] [C-M-down] [C-M-right] [C-M-left] [C-S-up] [C-S-down] [C-S-right] [C-S-left] [M-S-up] [M-S-down] [M-S-right] [M-S-left] [C-M-S-up] [C-M-S-down] [C-M-S-right] [C-M-S-left] [C-home] [M-home] [S-home] [C-M-home] [C-S-home] [M-S-home] [C-M-S-home] [C-end] [M-end] [S-end] [C-M-end] [C-S-end] [M-S-end] [C-M-S-end] [C-prior] [M-prior] [S-prior] [C-M-prior] [C-S-prior] [M-S-prior] [C-M-S-prior] [C-next] [M-next] [S-next] [C-M-next] [C-S-next] [M-S-next] [C-M-S-next])) (eat-update-semi-char-mode-map) (eat-eshell-update-semi-char-mode-map) (eat-reload)
eshell
- after each command finished processing, give audio feedback and tell us how many lines of output are done.
- have a command in eshell (d for done) to append as " ; d" to get audio feedback, when command has finished, optionally provide a text to be spoken after "d", defaults to "done.".
(cl-defun eshell/d (&optional (text "done.")) (speechd-say-sound "at" :priority 'important) (speechd-say-text text :priority 'important)) (require 'em-alias) ;;(eshell/alias "ls" "ls -1 $@*") (eshell-read-aliases-list) (defun okflo-eshell-post-command-hook () (when global-speechd-speak-mode (speechd-say-sound "at" :priority 'important) (speechd-say-text (format "output %s lines" (count-lines (eshell-end-of-output) (eshell-beginning-of-output))) :priority 'important ))) (add-hook 'eshell-post-command-hook 'okflo-eshell-post-command-hook) (setf eshell-highlight-prompt t)
elfeed
- change entries in search-view, so that title gets spoken first.
- elfeed-search-remain-on-entry needs to be set to t, otherwise following entries are spoken in entry-view
(require 'elfeed) (setf elfeed-search-title-max-width 150) (defun okflo-elfeed-search-print-entry (entry) "Print ENTRY to the buffer." (let* ((date (elfeed-search-format-date (elfeed-entry-date entry))) (title (or (elfeed-meta entry :title) (elfeed-entry-title entry) "")) (title-faces (elfeed-search--faces (elfeed-entry-tags entry))) (feed (elfeed-entry-feed entry)) (feed-title (when feed (or (elfeed-meta feed :title) (elfeed-feed-title feed)))) (tags (mapcar #'symbol-name (elfeed-entry-tags entry))) (tags-str (mapconcat (lambda (s) (propertize s 'face 'elfeed-search-tag-face)) tags ",")) (title-width (- (window-width) 10 elfeed-search-trailing-width)) (title-column (elfeed-format-column title (elfeed-clamp elfeed-search-title-min-width title-width elfeed-search-title-max-width) :left))) (insert (propertize title-column 'face title-faces 'kbd-help title) " ") (when feed-title (insert (propertize feed-title 'face 'elfeed-search-feed-face) " ")) (insert (propertize date 'face 'elfeed-search-date-face) " ") (when tags (insert "(" tags-str ")")))) (setf elfeed-search-print-entry-function #'okflo-elfeed-search-print-entry) (setf elfeed-search-remain-on-entry t)
org-mode
- get feedback, whether entry is folded, bound to <C-c f> in org-buffers.
(defun okflo-say-org-fold-status () (interactive) (save-excursion (end-of-line) (if (org-fold-folded-p) (speechd-say-text "folded" :priority 'important) (speechd-say-text "not folded" :priority 'important)))) (define-key org-mode-map (kbd "C-c f") 'okflo-say-org-fold-status)
EWW
- have an audio-feedback, when load/rendering of page is done.
- <j> for jumping to next H* tags
- j jumps to next H1 tag
- N prefix + j jums to next HN tag, f.e. <3j> jumps to next H3 tag.
(require 'eww) (defun okflo-eww-page-loaded () (when global-speechd-speak-mode (speechd-say-sound "piano-3.wav" :priority 'important) (speechd-say-text "page loaded" :priority 'important))) (add-hook 'eww-after-render-hook #'okflo-eww-page-loaded) (defun okflo-eww-move-cursor-to-next-heading (level) (interactive "P") (setf level (or level 1)) (let ((next-match (text-property-search-forward 'outline-level level t t))) (setf mydebug next-match) (if next-match (goto-char (prop-match-beginning next-match)) (message "No match")))) (define-key eww-mode-map (kbd "j") 'okflo-eww-move-cursor-to-next-heading)
mu4e
- when viewing a message speak its subject
(require 'mu4e) (setf mu4e-headers-fields '((:from-or-to . 30) (:thread-subject . 70) (:flags . 6) (:human-date . 12))) (defun okflo-mu4e-speak-subject () (when global-speechd-speak-mode (speechd-say-text (cl-getf (mu4e-message-at-point) :subject) :priority 'important))) (add-hook 'mu4e-view-rendered-hook 'okflo-mu4e-speak-subject)
exwm
I use exwm as my window manager.
- <s-[0-9]> jumps to frame [0-9] giving audio feedback.
- <s-^> switches display on/off by calling xrandr.
(speechd-speak-function-feedback exwm-workspace-switch-create after (progn (speechd-say-sound "hash" :priority 'important) (message (format "%s" exwm-workspace-current-index)))) (defvar okflo-display t) (defun okflo-switch-display-on/off () (interactive) (if okflo-display (progn (call-process "/usr/bin/xrandr" nil nil nil "--output" "eDP1" "--off") (setf okflo-display nil) (speechd-say-text "Display switched off")) (progn (call-process "/usr/bin/xrandr" nil nil nil "--output" "eDP1" "--auto") (setf okflo-display t) (speechd-say-text "Display switched on")))) (global-set-key (kbd "s-^") 'okflo-switch-display-on/off)
finally
- switch to my preferrd voice
- start speechd-el
(okflo-switch-to-english) (speechd-speak)