emacs-nano-tts-minor-mode

A text-to-speech accessibility tool which reads aloud the active region.
git clone https://kaka.farm/~git/emacs-nano-tts-minor-mode
Log | Files | Refs | LICENSE

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