LINUX.ORG.RU

[лисп?] Festival text2wave. Как оно работает?


0

0

В комплекте к festival идёт такой скрипт text2wave с большим количеством скобок:

(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)
У него большой недостаток: время работы растёт от длиины обрабатываемого файла быстрее чем линейно. Поэтому вопрос: как оно работает?

С лиспом дела никогда не имел.

  • Понятно, что функция main главная, ка в программе на С.
  • Понятно, что вначале онв вызывает get_options, которая или выводит подсказку через text2wave_help, или анализирует параметры командной строки.
  • Потом многократно, для каждоо файла из списка text_files ,вызвыается tts_file с параметром mode или с заданным умолчальным списком параметров.
  • Затем они ввобираются воедино функцией combine_waves
    • Задаётся локальная переменная wholeutt, в неё вводится пустой текст.
    • Затем для каждого файла в списке wavefiles, записанном задом наперёд, вызывается функция utt.import.wave, которая записывает этот файл в конец wholeutt.
    • Дальше идёт функция изменения частоты, но я её не задействовал, поэтому она не должна была вызыватьмя, т.к. (defvar frequency nil).
    • Далее идёт изменение громгости, ео я его ноже не менял, поэтому (defvar volume «1.0»).
    • Потом utt.save.wave сохраняет записанный во wholeutt звук в вэйвфайл.

Теперь вопрос: на создание промежуточных файлов уходят секунды, от силы —минуты. На их сборку воедино — на 1-2 порядка больше, и чем их больше, тем больше времени требуется на каждый байт текста. При ээтом объём свопфайла может скакать туда-сюда на полгигабайта. Как ускорить процесс сборки файлов с отдельными строками в целый большой файл?

★★★★★

Вообще такие вопросы нужно решать с профилёром, а не чтением кода. Oprofile например подойдёт.

Для объединения вызывается wave.add, что в свою очередь вызывает оператор += из EST_Wave из speech_tools. Что в свою очередь вызывает EST_TMatrix<T> &EST_TMatrix<T>::add_rows(const EST_TMatrix<T> &in) оттуда же. Возможно там больше всего времени и тратится.

welkam ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.