;;; ;;; File: festival.el ;;; Emacs Lisp ;;; ;;; Alan W Black CSTR (awb@cstr.ed.ac.uk) June 1996 ;;; ;;; Provide an emacs mode for interfacing to the festival speech ;;; synthesizer system ;;; ;;; I've looked at many examples from the emacs Lisp directory ;;; copying relevant bits from here and there, so this can only ;;; reasonably inherit the GNU licence (GPL) ;;; ;;; Setup: ;;; In your .emacs add the following 2 lines to get a Say menu: ;;; ;;; (autoload 'say-minor-mode "festival" "Menu for using Festival." t) ;;; (say-minor-mode t) ;;; (setq auto-mode-alist ;;; (append '(("\\.festivalrc$" . scheme-mode)) auto-mode-alist)) ;;; ;;; The following gives you pretty colors in emacs-19 if you are into ;;; such things ;;; ;;; Some colors for scheme mode ;;; (hilit-set-mode-patterns ;;; '(scheme-mode) ;;; '( ;;; (";.*" nil comment) ;;; (hilit-string-find ?\\ string) ;;; ("^\\s *(def\\s +" "\\()\\|nil\\)" defun) ;;; ("^\\s *(defvar\\s +\\S +" nil decl) ;;; ("^\\s *(set\\s +\\S +" nil decl) ;;; ("^\\s *(defconst\\s +\\S +" nil define) ;;; ("^\\s *(\\(provide\\|require\\).*$" nil include) ;;; ("(\\(let\\*?\\|cond\\|if\\|or\\|and\\|map\\(car\\|concat\\)\\|prog[n1*]?\\|while\\|lambda\\|function\\|Parameter\\|set\\([qf]\\|car\\|cdr\\)?\\|nconc\\|eval-when-compile\\|condition-case\\|unwind-protect\\|catch\\|throw\\|error\\)[ \t\n]" 1 keyword))) ;;; ;;; ;;;-------------------------------------------------------------------- ;;; Copyright (C) Alan W Black 1996 ;;; This code is distributed in the hope that it will be useful, ;;; but WITHOUT ANY WARRANTY. No author or distributor accepts ;;; responsibility to anyone for the consequences of using this code ;;; or for whether it serves any particular purpose or works at all, ;;; unless explicitly stated in a written agreement. ;;; ;;; Everyone is granted permission to copy, modify and redistribute ;;; this code, but only under the conditions described in the GNU ;;; Emacs General Public License. A copy of this license is ;;; distrubuted with GNU Emacs so you can know your rights and ;;; responsibilities. It should be in a file named COPYING. Among ;;; other things, the copyright notice and this notice must be ;;; preserved on all copies. ;;;-------------------------------------------------------------------- ;;; (defvar festival-program-name "festival") (defvar festival-process nil) (defvar festival-tmp-file (format "/tmp/festival-emacs-tmp-%s" (user-real-login-name)) "Filename to save input for Festivial.") (defun festival-fast () (interactive) (festival-send-command '(Parameter.set 'Duration.Stretch 0.8))) (defun festival-slow () (interactive) (festival-send-command '(Parameter.set 'Duration.Stretch 1.2))) (defun festival-ndur () (interactive) (festival-send-command '(Parameter.set 'Duration.Stretch 1.0))) (defun festival-intro () (interactive) (festival-send-command '(intro))) (defun festival-gsw () (interactive) (festival-send-command '(voice_gsw_diphone))) (defun festival-rab () (interactive) (festival-send-command '(voice_rab_diphone))) (defun festival-ked () (interactive) (festival-send-command '(voice_ked_diphone))) (defun festival-kal () (interactive) (festival-send-command '(voice_kal_diphone))) (defun festival-don () (interactive) (festival-send-command '(voice_don_diphone))) (defun festival-welsh () (interactive) (festival-send-command '(voice_welsh_hl))) (defun festival-spanish () (interactive) (festival-send-command '(voice_spanish_el))) (defun festival-say-string (string) "Send string to festival and have it said" (interactive "sSay: ") (festival-start-process) (process-send-string festival-process (concat "(SayText " (format "%S" string) ") "))) (defun festival-send-command (cmd) "Send command to festival" (interactive "px") (festival-start-process) (process-send-string festival-process (format "%S " cmd))) (defun festival-process-status () (interactive) (if festival-process (message (format "Festival process status: %s" (process-status festival-process))) (message (format "Festival process status: NONE")))) (defun festival-start-process () "Check status of process and start it if necessary" (interactive ) (let ((process-connection-type t)) (if (and festival-process (eq (process-status festival-process) 'run)) 't ;;(festival-kill-festival t) (message "Starting new synthesizer process...") (sit-for 0) (setq festival-process (start-process "festival" (get-buffer-create "*festival*") festival-program-name))) )) (defun festival-kill-process () "Kill festival sub-process" (interactive) (if festival-process (kill-process festival-process)) (setq festival-process nil) (message "Festival process killed")) (defun festival-send-string (string) "Send given string to fesitval process." (interactive) (festival-start-process) (process-send-string festival-process string)) (defun festival-say-region (reg-start reg-end) "Send given region to festival for saying. This saves the region as a file in /tmp and then tells festival to say that file. The major mode is *not* passed as text mode name to Festival." (interactive "r") (write-region reg-start reg-end festival-tmp-file) (festival-send-command (list 'tts festival-tmp-file nil))) (defun festival-say-buffer () "Send given region to festival for saying. This saves the region as a file in /tmp and then tells festival to say that file. The major-mode is passed as a text mode to Festival." (interactive) (write-region (point-min) (point-max) festival-tmp-file) ;; Because there may by sgml-like sub-files mentioned ;; ensure festival tracks the buffer's default-directory (festival-send-command (list 'cd (expand-file-name default-directory))) (if (equal "-mode" (substring (format "%S" major-mode) -5 nil)) (if (equal "sgml" (substring (format "%S" major-mode) 0 -5)) (festival-send-command (list 'tts festival-tmp-file "sable")) (festival-send-command (list 'tts festival-tmp-file (substring (format "%S" major-mode) 0 -5)))) (festival-send-command (list 'tts festival-tmp-file nil)))) ;; ;; say-minor-mode provides a menu offering various speech synthesis commands ;; (defvar say-minor-mode nil) (defun say-minor-mode (arg) "Toggle say minor mode. With arg, turn say-minor-mode on iff arg is positive." (interactive "P") (setq say-minor-mode (if (if (null arg) (not say-minor-mode) (> (prefix-numeric-value arg) 0)) t)) (force-mode-line-update)) (setq say-params-menu (make-sparse-keymap "Pitch/Duration")) (fset 'say-params-menu (symbol-value 'say-params-menu)) (define-key say-params-menu [say-fast] '("Fast" . festival-fast)) (define-key say-params-menu [say-slow] '("Slow" . festival-slow)) (define-key say-params-menu [say-ndur] '("Normal Dur" . festival-ndur)) (setq say-lang-menu (make-sparse-keymap "Select language")) (fset 'say-lang-menu (symbol-value 'say-lang-menu)) (define-key say-lang-menu [say-lang-spain1] '("Spanish el" . festival-spanish)) (define-key say-lang-menu [say-lang-welsh1] '("Welsh hl" . festival-welsh)) (define-key say-lang-menu [say-lang-eng5] '("English gsw" . festival-gsw)) (define-key say-lang-menu [say-lang-eng4] '("English don" . festival-don)) (define-key say-lang-menu [say-lang-eng3] '("English rab" . festival-rab)) (define-key say-lang-menu [say-lang-eng2] '("English ked" . festival-ked)) (define-key say-lang-menu [say-lang-eng1] '("English kal" . festival-kal)) ;(define-key say-params-menu [say-set-dur-stretch] ; '("Set Duration Stretch" . festival-set-dur-stretch)) ;(define-key say-params-menu [say-high] '("High" . festival-high)) ;(define-key say-params-menu [say-low] '("Low" . festival-low)) ;(define-key say-params-menu [say-npit] '("Normal Pitch" . festival-npit)) ;(define-key say-params-menu [say-set-pitch-stretch] ; '("Set Pitch Stretch" . festival-set-pitch-stretch)) (setq say-minor-mode-map (make-sparse-keymap)) (setq say-menu (make-sparse-keymap "SAY")) (define-key say-minor-mode-map [menu-bar SAY] (cons "Say" say-menu)) (define-key say-minor-mode-map [menu-bar SAY festival-intro] '("Festival Intro" . festival-intro)) (define-key say-minor-mode-map [menu-bar SAY festival-process-status] '("Festival status" . festival-process-status)) (define-key say-minor-mode-map [menu-bar SAY festival-kill-process] '("Kill Festival" . festival-kill-process)) (define-key say-minor-mode-map [menu-bar SAY festival-start-process] '("(Re)start Festival" . festival-start-process)) ;;(define-key say-menu [separator-process] '("--")) ;;(define-key say-menu [params] '("Pitch/Durations" . say-params-menu)) (define-key say-menu [separator-buffers] '("--")) (define-key say-menu [festival-send-command] '("Festival eval command" . festival-send-command)) (define-key say-menu [say-lang-menu] '("Select language" . say-lang-menu)) (define-key say-menu [festival-say-buffer] '("Say buffer" . festival-say-buffer)) (define-key say-menu [festival-say-region] '("Say region" . festival-say-region)) (setq minor-mode-map-alist (cons (cons 'say-minor-mode say-minor-mode-map) minor-mode-map-alist)) (or (assq 'say-minor-mode minor-mode-alist) (setq minor-mode-alist (cons '(say-minor-mode "") minor-mode-alist))) ;;; ;;; A FESTIVAL inferior mode (copied from prolog.el) ;;; (defvar inferior-festival-mode-map nil) (defun inferior-festival-mode () "Major mode for interacting with an inferior FESTIVAL process. The following commands are available: \\{inferior-festival-mode-map} Entry to this mode calls the value of `festival-mode-hook' with no arguments, if that value is non-nil. Likewise with the value of `comint-mode-hook'. `festival-mode-hook' is called after `comint-mode-hook'. You can send text to the inferior FESTIVAL from other buffers using the commands `send-region', `send-string' Return at end of buffer sends line as input. Return not at end copies rest of line to end and sends it. \\[comint-kill-input] and \\[backward-kill-word] are kill commands, imitating normal Unix input editing. \\[comint-interrupt-subjob] interrupts the shell or its current subjob if any. \\[comint-stop-subjob] stops. \\[comint-quit-subjob] sends quit signal." (interactive) (require 'comint) (comint-mode) (setq major-mode 'inferior-festival-mode mode-name "Inferior FESTIVAL" comint-prompt-regexp "^festival> ") (if inferior-festival-mode-map nil (setq inferior-festival-mode-map (copy-keymap comint-mode-map)) (festival-mode-commands inferior-festival-mode-map)) (use-local-map inferior-festivalr-mode-map) (run-hooks 'festival-mode-hook)) ;;;###autoload (defun run-festival () "Run an inferior FESTIVAL process, input and output via buffer *festival*." (interactive) (require 'comint) (switch-to-buffer (make-comint "festival" festival-program-name)) (inferior-festival-mode)) (provide 'festival)