[spoiler]
Устанавливаю на систему Centos 5.7. В репозитории валяется festival v1.95, который не может синтезировать русскую речь. Поэтому буду ставить из исходников.
Итак, выкачиваем исходники:
cd /usr/src wget http://www.cstr.ed.ac.uk/downloads/festival/2.1/speech_tools-2.1-release.tar.gz wget http://www.cstr.ed.ac.uk/downloads/festival/2.1/festival-2.1-release.tar.gz |
Перед компиляцией у меня потребовалось установить ncurses-devel, ну а так же в системе должны присутствовать gcc и gcc-g++
yum install ncurses-devel |
Далее распаковываем, компилируем
tar zxvf festival-2.1-release.tar.gz tar zxvf speech_tools-2.1-release.tar.gz cd speech_tools ./configure make make install cd .. cd festival ./configure make make install |
По желанию добавляем в PACH путь до бинарников
export PATH=$PATH:/usr/src/festival/bin/ |
Далее скачиваем русский язык:
cd /usr/src wget http://sourceforge.net/projects/festlang.berlios/files/msu_ru_nsh_clunits-0.5.tar.bz2 |
Теперь нужно создать директорию для голоса и озвучки
mkdir ./festival/lib/voices/ mkdir ./festival/lib/voices/russian |
Распаковываем туда нашу озвучку
tar xjf msu_ru_nsh_clunits-0.5.tar.bz2 -C ./festival/lib/voices/russian |
Должно получиться вот-так
ls /usr/src/festival/voices/russian/msu_ru_nsh_clunits/ COPYING dict etc festival festvox lab lib mcep README wav |
Далее добавляем в начало файла ./festival/lib/languages.scm
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (define (language_russian) "(language_russian) Set up language parameters for Russian." (set! male1 voice_msu_ru_nsh_clunits) (male1) (Parameter.set 'Language 'russian) ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
И в этот же файл в конце после
(language_british_english)) ((equal? language 'americanenglish) |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ((equal? language 'russian) (language_russian)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; |
nano ~/.festivalrc (set! voice_default 'voice_msu_ru_nsh_clunits) |
nano /etc/asterisk/festival.conf |
............. ; Use cache (yes, no - defaults to no) ; usecache=yes ; cachedir=/var/lib/asterisk/festivalcache/ ; festivalcommand=(tts_textasterisk "%s" 'file)(quit)\n ; ; |
mkdir /var/lib/asterisk/festivalcache/&& chown asterisk:asterisk /var/lib/asterisk/festivalcache/ |
Ну а теперь загружаем модуль app_festival в астериске, если не загружен
asterisk -r sip4*CLI> module load app_festival.so |
Стартуем фестиваль
festival -b '(voice_msu_ru_nsh_clunits)' --server &>/dev/null |
Все, создаем диалплан, слушаем в трубке голос диктора
[fest] exten = 1111,1,Answer exten = 1111,n,Festival('Первое слово. Второе слово. Третье слово.') exten = 1111,n,Hangup |
Парочка примеров синтеза речи festival:
Пример 1
Пример 2
Далее попробуем управлять синтезом речи непосредственно из самой SDK
В простейшем случае эта задача сводится к следующему алгоритму:
1)Формируем wav файл с синтезированным голосовым сообщением средствами скрипта text2wave
2)Инициируем вызов на клиента и воспроизводим ему свой сгенерированный файл
Итак, для начала создадим скрипт text2wave в /usr/local/bin
#!/usr/bin/festival --script ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-*-mode:scheme-*- ;; ;; ;; Centre for Speech Technology Research ;; ;; University of Edinburgh, UK ;; ;; Copyright © 1996,1997 ;; ;; All Rights Reserved. ;; ;; ;; ;; Permission is hereby granted, free of charge, to use and distribute ;; ;; this software and its documentation without restriction, including ;; ;; without limitation the rights to use, copy, modify, merge, publish, ;; ;; distribute, sublicense, and/or sell copies of this work, and to ;; ;; permit persons to whom this work is furnished to do so, subject to ;; ;; the following conditions: ;; ;; 1. The code must retain the above copyright notice, this list of ;; ;; conditions and the following disclaimer. ;; ;; 2. Any modifications must be clearly marked as such. ;; ;; 3. Original authors' names are not deleted. ;; ;; 4. The authors' names are not used to endorse or promote products ;; ;; derived from this software without specific prior written ;; ;; permission. ;; ;; ;; ;; THE UNIVERSITY OF EDINBURGH AND THE CONTRIBUTORS TO THIS WORK ;; ;; DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ;; ;; ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT ;; ;; SHALL THE UNIVERSITY OF EDINBURGH NOR THE CONTRIBUTORS BE LIABLE ;; ;; FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ;; ;; WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN ;; ;; AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ;; ;; ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF ;; ;; THIS SOFTWARE. ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Author: Alan W Black ;;; Date: November 1997 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ;;; Text to a single waveform like festival_client but without ;;; starting hte server ;;; ;;; Because this is a --script type file I has to explicitly ;;; load the initfiles: init.scm and user's .festivalrc (load (path-append libdir "init.scm")) ;;; Process command line arguments (define (text2wave_help) (format t "%s\n" "text2wave [options] textfile Convert a textfile to a waveform Options -mode <string> Explicit tts mode. -o ofile File to save waveform (default is stdout). -otype <string> Output waveform type: ulaw, snd, aiff, riff, nist etc. (default is riff) -F <int> Output frequency. -scale <float> Volume factor -eval <string> File or lisp s-expression to be evaluated before synthesis. ") (quit)) ;;; No gc messages (gc-status nil) ;;; Default argument values (defvar outfile "-") (defvar output_type 'riff) (defvar frequency nil) ;; default is no frequency modification (defvar text_files '("-")) (defvar mode nil) (defvar volume "1.0") (defvar wavefiles nil) ;;; Get options (define (get_options) (let ((files nil) (o argv)) (if (or (member_string "-h" argv) (member_string "-help" argv) (member_string "--help" argv) (member_string "-?" argv)) (text2wave_help)) (while o (begin (cond ((string-equal "-o" (car o)) (if (not (cdr o)) (text2wave_error "no output file specified")) (set! outfile (car (cdr o))) (set! o (cdr o))) ((string-equal "-otype" (car o)) (if (not (cdr o)) (text2wave_error "no output filetype specified")) (set! output_type (car (cdr o))) (set! o (cdr o))) ((or (string-equal "-f" (car o)) ;; for compatibility and memory loss (string-equal "-F" (car o))) (if (not (cdr o)) (text2wave_error "no frequency specified")) (set! frequency (car (cdr o))) (set! o (cdr o))) ((string-equal "-scale" (car o)) (if (not (cdr o)) (text2wave_error "no scale specified")) (set! volume (car (cdr o))) (set! o (cdr o))) ((string-equal "-mode" (car o)) (if (not (cdr o)) (text2wave_error "no mode specified")) (set! mode (car (cdr o))) (set! o (cdr o))) ((string-equal "-eval" (car o)) (if (not (cdr o)) (text2wave_error "no file specified to load")) (if (string-matches (car (cdr o)) "^(.*") (eval (read-from-string (car (cdr o)))) (load (car (cdr o)))) (set! o (cdr o))) (t (set! files (cons (car o) files)))) (set! o (cdr o)))) (if files (set! text_files (reverse files))))) (define (text2wave_error message) (format stderr "%s: %s\n" "text2wave" message) (text2wave_help)) (define (save_record_wave utt) "Saves the waveform and records its so it can be joined into a a single waveform at the end." (let ((fn (make_tmp_filename))) (utt.save.wave utt fn) (set! wavefiles (cons fn wavefiles)) utt)) (define (combine_waves) "Join all the waves together into the desired output file and delete the intermediate ones." (let ((wholeutt (utt.synth (Utterance Text "")))) (mapcar (lambda (d) (utt.import.wave wholeutt d t) (delete-file d)) (reverse wavefiles)) (if frequency (utt.wave.resample wholeutt (parse-number frequency))) (if (not (equal? volume "1.0")) (begin (utt.wave.rescale wholeutt (parse-number volume)))) (utt.save.wave wholeutt outfile output_type) )) ;;; ;;; Redefine what happens to utterances during text to speech ;;; (set! tts_hooks (list utt.synth save_record_wave)) (define (main) (get_options) ;; do the synthesis (mapcar (lambda (f) (if mode (tts_file f mode) (tts_file f (tts_find_text_mode f auto-text-mode-alist)))) text_files) ;; Now put the waveforms together at again (combine_waves) ) ;;; Do the work (main) |
Далее добавим простейший диалплан, к примеру в контексте [mikoajamdll]
exten => 1111,1,NoCDR() exten => 1111,n,Answer exten => 1111,n,System("echo "${var}" |text2wave -eval '(voice_msu_ru_nsh_clunits)' -F 8000 -o /var/spool/asterisk/monitor/name.wav") exten => 1111,n,Hangup |
Далее делаем релоад диалпланов
asterisk -rx'dialplan reload' |
Ну а теперь проверяем работу при помощи нашей SDK:
Указываем настройка сервера телефонии, авторизовываемся, и вставляем в поле "Произвольная команда" следующую строку
Action=Originate&Async=0&CallerID=SIP/8888&Channel=Local/999@mikoajamdll&Context= mikoajamdll&Exten=1111&Priority=1&Variable=var=%D0%A1%D0%BA%D0%B0%D0%B7+ %D0%BE+%D1%82%D0%BE%D0%BC+%D0%BA%D0%B0%D0%BA+%D0BE%D0%B4%D0%B8%D0%BD +%D0%BC%D1%83%D0%B6%D0%B8%D0%BA+%D0%B4%D0%B2%D1%83%D1%85+ %D0%B3%D0%B5%D0%BD%D0%B5%D1%80%2B%D0%B0%D0%BB%D0%BE%D0%B2+ %D0%BF%D1%80%D0%BE%D0%BA%D0%BE%D1%80%D0%BC%D0%B8%D0%BB. |
Все, файл /var/spool/asterisk/monitor/name.wav создан, теперь слушаем его на телефоне. Для этого в поле "Произвольная команда" вставим другую строку
Action=Originate&CallerID=SIP/8888&Context=from-internal&Exten=7777&Priority=1&Application=Playback&Data=/var/spool/asterisk/monitor/name.wav |
Далее вы можете настраивать диалплан, посылать произвольные команды, в зависимости от вашей задачи