Выбрать главу

Как оказывается, весь функционал этой системы и пакет разнообразных голосов доступны также из командной строки, через встроенную утилиту say. Ее можно проверить следующей командой:

$ say "You never knew I could talk to you, did you?"

Мы знали, что вам это понравится!

С этой программой можно сделать много чего, но для начала напишем сценарий-обертку, который поможет выявить установленные голоса и послушать, как они звучат. Сценарий в листинге 13.10 не заменяет команду say; он лишь упрощает работу с ней (обычное дело для подобных сценариев в этой книге).

Код

Листинг 13.10. Сценарий sayit

#!/bin/bash

# sayit — использует команду "say" для всего, что будет указано (только для OS X).

dosay="$(which say) −quality=127"

format="$(which fmt) −w 70"

voice="" # По умолчанию системный голос.

rate=""··# По умолчанию стандартная скорость произношения.

demovoices()

{

··# Предлагает прослушать произношение каждым доступным голосом.

··voicelist=$(say −v \? | grep "en_" |  −c— c1-12 \

····| sed 's/ /_/;s/ //g;s/_$//')

··if ["$1" = "list"]; then

····echo "Available voices: $(echo $voicelist | sed 's/ /, /g;s/_/ /g') \

······| $format"

····echo "HANDY TIP: use \"$(basename $0) demo\" to hear all the voices"

····exit 0

··fi

··for name in $voicelist; do

····myname=$(echo $name | sed 's/_/ /')

····echo "Voice: $myname"

····$dosay −v "$myname" "Hello! I'm $myname. This is what I sound like."

··done

··exit 0

}

usage()

{

··echo "Usage: sayit [-v voice] [-r rate] [-f file] phrase"

··echo " or: sayit demo"

··exit 0

}

while getopts "df: r: v: " opt; do

··case $opt in

····d) demovoices list··;;

····f) input="$OPTARG"··;;

····r) rate="-r $OPTARG";;

····v) voice="$OPTARG"··;;

··esac

done

shift $(($OPTIND — 1))

if [$# −eq 0 −a — z "$input"]; then

··$dosay "Hey! You haven't given me any parameters to work with."

··echo "Error: no parameters specified. Specify a file or phrase."

··exit 0

fi

if ["$1" = "demo"]; then

··demovoices

fi

if [! -z "$input"]; then

··$dosay $rate −v "$voice" −f $input

else

··$dosay $rate −v "$voice" "$*"

fi

exit 0

Как это работает

В действительности в системе установлено больше голосов, чем указано в сводке (просто здесь перечислены голоса, оптимизированные для английского языка). Чтобы получить полный список, выполните оригинальную команду say с параметрами −v \?. Ниже приводится сокращенная версия полного списка голосов:

$ say −v \?

Agnes······en_US··# Разве не прекрасно иметь компьютер, который говорит с вами?

Albert···· en_US··# В моем горле живет лягушка. Да, самая настоящая лягушка!

Alex······ en_US··# Многие узнают меня по голосу.

Alice······it_IT··# Привет, меня зовут Алиса, я говорю по-итальянски.

-опущено-

Zarvox···· en_US··# Это похоже на мирную планету.

Zuzana···· cs_CZ··# Добрый день, меня зовут Сюзанна. Я говорю по-чешски.

$

Наши любимые дикторы — Пайп Орган (Pipe Organ, «Мы должны радоваться этому болезненному голосу») и Зарвокс (Zarvox, «Это похоже на мирную планету»).

Очевидно, что на выбор дается очень много голосов. Плюс, у некоторых из них очень неправильное английское произношение. Одно из решений этой проблемы — фильтровать дикторов по "en_" (или другому языку по вашему выбору), чтобы получить только голоса с нужным произношением. Для выбора американского английского языка можно использовать фильтр "en_US", но другие английские голоса тоже стоит послушать. Полный список голосов извлекается в строке .

Мы добавили в конец блока сложную череду подстановок с помощью sed, потому что этот список построен не совсем правильно: он включает имена, состоящие из одного (Fiona) и из двух слов (Bad News), а также дополнительные пробелы для выравнивания вывода по колонкам. Чтобы решить эту проблему, первый пробел в каждой строке заменяется символом подчеркивания, а все остальные пробелы удаляются. Если имя голоса состоит из одного слова, оно будет выглядеть так: "Ralph_", и заключительная подстановка sed удалит любые завершающие подчеркивания. В конце процесса остаются имена из двух слов с подчеркиванием, поэтому их нужно исправить перед выводом на экран. Однако код имеет интересный побочный эффект, в результате которого цикл while проще написать, использовав в качестве разделителя пробел.