nano-tts.el (4404B)
1 ;;; nano-tts.el --- A text-to-speech accessibility tool which reads aloud the active region. -*- lexical-binding: t; -*- 2 3 ;; Copyright (C) 2023 Yuval Langer 4 5 ;; Author: Yuval Langer <yuval.langer@gmail.com> 6 ;; Version: 1.0.0 7 ;; Keywords: convenience, multimedia 8 ;; URL: https://sr.ht/~kakafarm/emacs-nano-tts-minor-mode/ 9 10 ;; This program is free software; you can redistribute it and/or modify 11 ;; it under the terms of the GNU General Public License as published by 12 ;; the Free Software Foundation, either version 3 of the License, or 13 ;; (at your option) any later version. 14 15 ;; This program is distributed in the hope that it will be useful, 16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of 17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 ;; GNU General Public License for more details. 19 20 ;; You should have received a copy of the GNU General Public License 21 ;; along with this program. If not, see <https://www.gnu.org/licenses/>. 22 23 ;;; Commentary: 24 25 ;; Usage: 26 ;; 27 ;; 1. Mark a region of text. 28 ;; 2. Press C-c C-c or C-c c to run the nano-tts-speak command. 29 ;; 3. Press C-c c or C-c k to stop the speaking process. 30 ;; 31 ;; You can run nano-tts-speak (C-c C-c or C-c c) several times while 32 ;; nano-tts is reading to add more text for it to read. 33 ;; 34 ;; Much more can be added and maybe it will be added, e.g. WPM 35 ;; customization, maybe better command and control... who knows! 36 37 ;;; Code: 38 39 40 (defcustom nano-tts-words-per-minute 41 175 42 "Words per minute spoken." 43 :type 'integer 44 :group 'nano-tts 45 ) 46 47 (defvar nano-tts--tts-process 48 nil 49 "The process of a running text-to-speech engine. 50 51 nil when no text-to-speech engine is running.") 52 53 54 (defun nano-tts--get-region-as-string-to-speak () 55 "Return the string inside the region. 56 57 Concatenates a newline to the end of the region's string to make 58 the espeak actually read the text sent. This is needed due to 59 espeak's stdandard input buffering." 60 (concat 61 (buffer-substring (region-beginning) 62 (region-end)) 63 ;; XXX: We add a newline because it seems espeak stdin is buffered 64 ;; on newlines(?). 65 "\n")) 66 67 68 (defun nano-tts-speak () 69 "Read aloud a region of text. 70 71 Start a new TTS process and send the region's text into its 72 stdin, otherwise send the same to the existing running TTS 73 process. If the process is not running, make nano-tts forget 74 about the last process." 75 (interactive) 76 77 (cond 78 (nano-tts--tts-process 79 ;; If there is already a TTS process. 80 (let ((tts-process-status (process-status nano-tts--tts-process))) 81 (pcase tts-process-status 82 ;; And if the process is running. 83 ('run 84 ;; Send the existing process the text. 85 (process-send-string nano-tts--tts-process 86 (nano-tts--get-region-as-string-to-speak))) 87 ;; If the TTS process is not running. 88 (_ 89 ;; Kill the TTS process before making nano-tts forget about 90 ;; it. 91 (nano-tts-kill))))) 92 93 ;; If there is no running TTS process. 94 (t 95 ;; Start the TTS process so that later we can send text to it. 96 (setq nano-tts--tts-process 97 (make-process 98 :name "espeak" 99 :command (list "espeak" 100 "-s" (number-to-string nano-tts-words-per-minute)))) 101 102 ;; Send text to it. 103 (process-send-string nano-tts--tts-process 104 (nano-tts--get-region-as-string-to-speak))))) 105 106 107 (defun nano-tts-kill () 108 "Stop speaking. 109 110 Set nano-tts's variable holding the process object to nil and 111 kill the process." 112 (interactive) 113 114 ;; If there is a TTS process object. 115 (when nano-tts--tts-process 116 (let ((old-tts-process nano-tts--tts-process)) 117 ;; Make nano-tts aware that it is dead. 118 (setq nano-tts--tts-process 119 nil) 120 ;; Kill the TTS process. 121 (kill-process old-tts-process) 122 (message (format "Kill nano-tts process: %S" 123 old-tts-process))))) 124 125 126 ;;;###autoload 127 (define-minor-mode nano-tts-mode 128 "A very small text to speech thingimajig." 129 :init-value nil 130 :lighter " ntts" 131 :group 'nano-tts 132 :keymap 133 (list (cons (kbd "C-c c") 134 'nano-tts-speak) 135 (cons (kbd "C-c C-c") 136 'nano-tts-speak) 137 (cons (kbd "C-c k") 138 'nano-tts-kill) 139 (cons (kbd "C-c C-k") 140 'nano-tts-kill))) 141 142 143 (provide 'nano-tts) 144 ;;; nano-tts.el ends here