четверг, 13 мая 2010 г.

[HOWTO] Учим компьютер говорить по-русски / Festival скрипты

Сначала просто написал скрипт будильника. Теперь попробую собрать инфу по разным скриптам с использованием синтезатора речи festival в одну тему.
Хотя на самом деле это уже сделано здесь
Ну да ладно. Итак начнем.


Установка festival и festvox-ru, исправление багов.

Пакеты festival и festvox-ru лежат в стандартных репозиториях UBUNTU Karmic. Ставим:

Код:
sudo apt-get install festival festvox-ru

Если у вас более ранний дистрибутив, то festvox-ru можно взять здесь (распаковать в /usr/share/festival/voices/russian/msu_ru_nsh_clunits)

Для полноценной поддержки русского языка нужно ещё дополнительно внести некоторые изменения в файлы festival

В файл /usr/share/festival/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)
)

и в define(select_language language) добавить пару строчек
Код:
((equal? language 'russian)

(language_russian))

В файл /usr/share/festival/festival.scm в конце добавить строки
Код:
(Parameter.set 'Audio_Method 'Audio_Command)

(Parameter.set 'Audio_Command "aplay -q -c 1 -t raw -f s16 -r $SR $FILE")

(Кликните, чтобы показать/скрыть)
Или эти параметры можно добавить в ~/.festivalrc. Этот файл создается специально для прописи этих параметров. Но не обязательно, можно сделать, как сказано выше.

На этом подготовка завершена. Приступим непосредственно к написанию скриптов.
Стоп, чуть не забыл. Чтобы не было лишних вопросов, заранее предупрежу (или напомню), скриптам нужно давать право на исполнение.
Код:
chmod +x script.sh
Теперь приступим.


Будильник

Создаем скрипт в домашнем каталоге
Код:
gedit alarm

(Кликните, чтобы показать/скрыть)
Код:
#!/bin/bash


##Чтобы поставить будильник, вводим в терминале команду "crontab -e"
##И прописываем нужные параметры:
##PATH=/sbin:/bin:/usr/sbin:/usr/bin
##SHELL=/bin/bash
### m h dom mon dow command
##0 7 * * 1-5 bash alarm
##0 10 * * 6-7 bash alarm


export DISPLAY=:0
export LANG=ru_RU.UTF-8


# Склоняем в соответствующем падеже слова "час" и "минута" # 2 раза :)))

check_date ()

{

CURR_HOUR=`date +%H`
CURR_MIN=`date +%M`
TEMP_HOUR="`echo $CURR_HOUR | colrm 1 1`"
TEMP_MIN="`echo $CURR_MIN | colrm 1 1`"

if [ "$CURR_HOUR" -eq "0" ] || [ "$CURR_HOUR" -eq "5" ] || [ "$CURR_HOUR" -eq "6" ] || [ "$CURR_HOUR" -eq "7" ] || [ "$CURR_HOUR" -eq "8" ] || [ "$CURR_HOUR" -eq "9" ] || [ "$CURR_HOUR" -eq "11" ] || [ "$CURR_HOUR" -eq "12" ] || [ "$CURR_HOUR" -eq "13" ] || [ "$CURR_HOUR" -eq "14" ] || [ "$TEMP_HOUR" -eq "0" ] || [ "$TEMP_HOUR" -eq "5" ] || [ "$TEMP_HOUR" -eq "6" ] || [ "$TEMP_HOUR" -eq "7" ] || [ "$TEMP_HOUR" -eq "8" ] || [ "$TEMP_HOUR" -eq "9" ]; then LC_HOUR="часов"

elif [ "$CURR_HOUR" -eq "2" ] || [ "$CURR_HOUR" -eq "3" ] || [ "$CURR_HOUR" -eq "4" ] || [ "$TEMP_HOUR" -eq "2" ] || [ "$TEMP_HOUR" -eq "3" ] || [ "$TEMP_HOUR" -eq "4" ]; then LC_HOUR="часа"

elif [ "$CURR_HOUR" -eq "1" ] || [ "$TEMP_HOUR" -eq "1" ]; then LC_HOUR="час"

fi


if [ "$CURR_MIN" -eq "5" ] || [ "$CURR_MIN" -eq "6" ] || [ "$CURR_MIN" -eq "7" ] || [ "$CURR_MIN" -eq "8" ] || [ "$CURR_MIN" -eq "9" ] || [ "$CURR_MIN" -eq "11" ] || [ "$CURR_MIN" -eq "12" ] || [ "$CURR_MIN" -eq "13" ] || [ "$CURR_MIN" -eq "14" ] || [ "$TEMP_MIN" -eq "0" ] || [ "$TEMP_MIN" -eq "5" ] || [ "$TEMP_MIN" -eq "6" ] || [ "$TEMP_MIN" -eq "7" ] || [ "$TEMP_MIN" -eq "8" ] || [ "$TEMP_MIN" -eq "9" ]; then LC_MINUTE="минут"

elif [ "$CURR_MIN" -eq "3" ] || [ "$CURR_MIN" -eq "4" ] || [ "$TEMP_MIN" -eq "3" ] || [ "$TEMP_MIN" -eq "4" ]; then LC_MINUTE="минуты"

fi


if [ "$CURR_MIN" -eq "1" ]; then DATE_TIME="$CURR_HOUR $LC_HOUR однa минута"

elif [ "$CURR_MIN" -eq "11" ]; then DATE_TIME="$CURR_HOUR $LC_HOUR $CURR_MIN $LC_MINUTE"

elif [ "$TEMP_MIN" -eq "1" ]; then DATE_TIME="$CURR_HOUR $LC_HOUR $(echo $CURR_MIN-1|bc) одна минута"

elif [ "$CURR_MIN" -eq "2" ]; then DATE_TIME="$CURR_HOUR $LC_HOUR две минуты"

elif [ "$CURR_MIN" -eq "12" ]; then DATE_TIME="$CURR_HOUR $LC_HOUR $CURR_MIN $LC_MINUTE"

elif [ "$TEMP_MIN" -eq "2" ]; then DATE_TIME="$CURR_HOUR $LC_HOUR $(echo $CURR_MIN-2|bc) две минуты"

elif [ "$CURR_MIN" -eq "00" ]; then DATE_TIME="$CURR_HOUR $LC_HOUR ровно"

else DATE_TIME="$CURR_HOUR $LC_HOUR $CURR_MIN $LC_MINUTE"

fi

}


# Получаем температуру в своем городе

CURR_TEMP="`wget -O - http://www.gismeteo.ru/city/daily/11975/ 2>/dev/null | grep '
' | sed -r 's/
(.[0-9]+).*/\1/g' | awk '{print $1}'`"

if [ "`echo $CURR_TEMP | sed -r 's/(.)[0-9]+/\1/g'`" == "-" ]; then TEMP_SIGN="минус"

else TEMP_SIGN=""

fi

CURR_DEGREE=""
CURR_DEGREE="`echo $CURR_TEMP | sed -r 's/.([0-9]+)/\1/g'`"


# Склоняем в соответствующем падеже слово "градус"

TEMP_DEGREE="`echo $CURR_DEGREE | colrm 1 1`"

if [ "$CURR_DEGREE" -eq "0" ] || [ "$CURR_DEGREE" -eq "5" ] || [ "$CURR_DEGREE" -eq "6" ] || [ "$CURR_DEGREE" -eq "7" ] || [ "$CURR_DEGREE" -eq "8" ] || [ "$CURR_DEGREE" -eq "9" ] || [ "$CURR_DEGREE" -eq "11" ] || [ "$CURR_DEGREE" -eq "12" ] || [ "$CURR_DEGREE" -eq "13" ] || [ "$CURR_DEGREE" -eq "14" ] || [ "$TEMP_DEGREE" -eq "0" ] || [ "$TEMP_DEGREE" -eq "5" ] || [ "$TEMP_DEGREE" -eq "6" ] || [ "$TEMP_DEGREE" -eq "7" ] || [ "$TEMP_DEGREE" -eq "8" ] || [ "$TEMP_DEGREE" -eq "9" ]; then LC_DEGREE="градусов"

elif [ "$CURR_DEGREE" -eq "2" ] || [ "$CURR_DEGREE" -eq "3" ] || [ "$CURR_DEGREE" -eq "4" ] || [ "$TEMP_DEGREE" -eq "2" ] || [ "$TEMP_DEGREE" -eq "3" ] || [ "$TEMP_DEGREE" -eq "4" ]; then LC_DEGREE="градуса"

elif [ "$CURR_DEGREE" -eq "1" ] || [ "$TEMP_DEGREE" -eq "1" ]; then LC_DEGREE="градус"

fi


#А теперь сам будильник

check_date
amixer sset Master 100% unmute
rhythmbox-client --set-volume .1
sleep 5
rhythmbox-client --play
sleep 2
rhythmbox-client --set-volume .2
sleep 2
rhythmbox-client --set-volume .3
sleep 2
rhythmbox-client --set-volume .4
sleep 2
rhythmbox-client --set-volume .5
sleep 2
rhythmbox-client --set-volume .6
sleep 2
rhythmbox-client --set-volume .7
sleep 2
rhythmbox-client --set-volume .8
sleep 2
rhythmbox-client --set-volume .9
sleep 2
rhythmbox-client --set-volume 1.0
sleep 20
rhythmbox-client --set-volume .8
sleep 1
rhythmbox-client --set-volume .6
sleep 1
rhythmbox-client --set-volume .4
sleep 1
rhythmbox-client --set-volume .2
sleep 1
echo "Доброе утро, Хозяин. Я надеюсь, что вы хорошо спали?" | festival --tts --language russian
echo "Сегодня `date +%A`" | festival --tts --language russian
echo "Время $DATE_TIME" | festival --tts --language russian
echo "Температура за окном $TEMP_SIGN $CURR_DEGREE $LC_DEGREE." | festival --tts --language russian
echo "Желаю вам удач нава дня" | festival --tts --language russian
sleep 1
rhythmbox-client --set-volume .4
sleep 1
rhythmbox-client --set-volume .6
sleep 1
rhythmbox-client --set-volume .8
sleep 1
rhythmbox-client --set-volume 1.0
sleep 200
check_date
rhythmbox-client --set-volume .8
sleep 1
rhythmbox-client --set-volume .6
sleep 1
rhythmbox-client --set-volume .4
sleep 1
rhythmbox-client --set-volume .2
sleep 1
echo "Хозяин, время $DATE_TIME. Вероятно уже пора вставать?" | festival --tts --language russian
sleep 1
rhythmbox-client --set-volume .4
sleep 1
rhythmbox-client --set-volume .6
sleep 1
rhythmbox-client --set-volume .8
sleep 1
rhythmbox-client --set-volume 1.0
exit 0
Пришлось нарушить правила русского языка ради более-менее нормального звочания festival Smiley

Естественно вместо "http://www.gismeteo.ru/city/daily/11975/" пишем адрес странички для своего города.
Вместо "Хозяин" нужно вставить своё имя. Хотя можно оставить как есть. Smiley

Чтобы поставить будильник, вводим в терминале команду "crontab -e"
И прописываем нужные параметры:
Код:
PATH=/sbin:/bin:/usr/sbin:/usr/bin

SHELL=/bin/bash
# m h dom mon dow command
0 7 * * 1-5 bash alarm
0 10 * * 6-7 bash alarm
Указываем нужное время, у меня в будние дни будильник срабатывает в 7:00, а в выходные в 10:00

При срабатывании будильника сначала включается плеер, какое-то время играет музыка, потом громкость снижается, и электронный голос приветствует и сообщает нам инфу по сегодняшнему дню, время, день, температура за окном. Затем снова играет плеер.


Озвучка открытия/закрытия крышки ноутбука

Открываем для редактирования файл /etc/acpi/lid.sh, для этого набираем в терминале:
Код:
sudo gedit /etc/acpi/lid.sh

Добавляем в этот файл после строки #!/bin/bash следующий текст:
Код:
grep -q closed /proc/acpi/button/lid/LID/state

if [ $? = 0 ]
then
echo "Зачем вы меня закрыли?" | festival --tts --language russian;
else
echo "Привет!" | festival --tts --language russian;
fi
Ну или можно вписать стандартные фразы типа "открыто/закрыто".
Соответственно что напишете, то и будет говорить ноутбук при открытии/закрытии крышки.


Проверка почты gmail

Создаем скрипт в домашнем каталоге

Код:
gedit gmail

(Кликните, чтобы показать/скрыть)
Код:
#!/bin/bash


#Чтобы запустить автоматическую проверку почты, вводим в терминале команду "crontab -e"
#И прописываем нужные параметры:
##PATH=/sbin:/bin:/usr/sbin:/usr/bin
##SHELL=/bin/bash
### m h dom mon dow command
##*/15 17-23 * * 1-5 bash gmail
##*/15 10-23 * * 6-7 bash gmail


export DISPLAY=:0
export LANG=ru_RU.UTF-8


#Проверяем почту

NUMB=`curl -u grin.sv@gmail.com:qweasdzxc --silent "https://mail.google.com/mail/feed/atom" | grep -c ""`

#Склоняем в соответствующем падеже слова "непрочитанные сообщения"

TEMP_NUMB="`echo $NUMB | colrm 1 1`"

if [ "$NUMB" -eq "0" ] || [ "$NUMB" -eq "5" ] || [ "$NUMB" -eq "6" ] || [ "$NUMB" -eq "7" ] || [ "$NUMB" -eq "8" ] || [ "$NUMB" -eq "9" ] || [ "$NUMB" -eq "11" ] || [ "$NUMB" -eq "12" ] || [ "$NUMB" -eq "13" ] || [ "$NUMB" -eq "14" ] || [ "$TEMP_NUMB" -eq "0" ] || [ "$TEMP_NUMB" -eq "5" ] || [ "$TEMP_NUMB" -eq "6" ] || [ "$TEMP_NUMB" -eq "7" ] || [ "$TEMP_NUMB" -eq "8" ] || [ "$TEMP_NUMB" -eq "9" ]; then MSG="непрочитанных сообщений"

elif [ "$NUMB" -eq "2" ] || [ "$NUMB" -eq "3" ] || [ "$NUMB" -eq "4" ] || [ "$TEMP_NUMB" -eq "2" ] || [ "$TEMP_NUMB" -eq "3" ] || [ "$TEMP_NUMB" -eq "4" ]; then MSG="непрочитанных сообщения"

fi


if [ "$NUMB" -eq "1" ]; then NUMB_MSG="одно непрочитанное сообщение"

elif [ "$NUMB" -eq "11" ]; then NUMB_MSG="`echo $NUMB $MSG`"

elif [ "$TEMP_NUMB" -eq "1" ]; then NUMB_MSG="$(echo $NUMB-1|bc), одно непрочитанное сообщение"

else NUMB_MSG="`echo $NUMB $MSG`"

fi


#Сообщение, одновременно проверка плееров.

if [ $NUMB -eq "0" ]; then :

else

if [ "`ps -A | grep -c totem`" -ne "0" ]; then totem --pause
elif [ "`ps -A | grep -c rhythmbox`" -ne "0" ]; then rhythmbox-client --pause
fi

echo "Хозяин, на вашем почтовом ящике имеется $NUMB_MSG." | festival --tts --language russian
sleep 1

if [ "`ps -A | grep -c totem`" -ne "0" ]; then totem --play
elif [ "`ps -A | grep -c rhythmbox`" -ne "0" ]; then rhythmbox-client --play
fi
fi

exit 0

И снова используем cron, чтобы запланировать автоматическую проверку. Вводим в терминале команду "crontab -e". И прописываем нужные параметры. В моем случае это:
Код:
PATH=/sbin:/bin:/usr/sbin:/usr/bin

SHELL=/bin/bash
# m h dom mon dow command
*/15 17-23 * * 1-5 bash gmail
*/15 10-23 * * 6-7 bash gmail

В данном скрипте предусмотрена приостановка работающих плееров totem и rhythmbox. Но немного кривовато.


Чтение всплывающих уведомлений

Нам необходимо создать два скрипта, один из которых мы потом добавим в автоматически-запускаемые приложения в GNOME.
Итак, скрипт первый (speech.sh):
(Кликните, чтобы показать/скрыть)
Код:
#!/bin/bash


# Добавить в запускаемые приложения: /home/user/run_speech.sh /home/user/speech.sh

COMMAND_COUNTER=0
MESSAGE_HEADER=""
MESSAGE_BODY=""
PARSE_REQUIRED=0

while read -r DBUS_MESSAGE ; do
ID=`echo $DBUS_MESSAGE | grep "uint32 0"`

if [[ $ID ]] ; then
let COMMAND_COUNTER=0
let PARSE_REQUIRED=1
else
if [ $PARSE_REQUIRED -eq 1 ] ; then
ID=`echo $DBUS_MESSAGE | egrep "string\ \""`

if [[ $ID ]] ; then
let COMMAND_COUNTER=$COMMAND_COUNTER+1
fi

if [ $COMMAND_COUNTER -eq 2 ] ; then
MESSAGE_HEADER=`echo $DBUS_MESSAGE | grep "string\ \"" | sed s/^string\ \"//g | sed s/\"$//g`
fi

if [ $COMMAND_COUNTER -eq 3 ] ; then
MESSAGE_BODY=`echo $DBUS_MESSAGE | grep "string\ \"" | sed s/^string\ \"//g | sed s/\"$//g`

echo $MESSAGE_HEADER | festival --tts --language russian
echo $MESSAGE_BODY | festival --tts --language russian

let COMMAND_COUNTER=$COMMAND_COUNTER+1
let PARSE_REQUIRED=0
fi
fi
fi
done

Суть этого скрипта на bash заключается в следующем. Он принимает со стандартного ввода данные, полученные из второго скрипта и затем парсит их. Затем формирует сообщение и отправляет его в festival для синтеза речевого сообщения. Данные для этого скрипта предоставляет второй скрипт (run_speech.sh), который очень короткий и выглядит так:
Код:
#!/bin/bash


dbus-monitor "interface='org.freedesktop.Notifications', member='Notify'" | $1

Что делает этот скрипт. Он в качестве параметра принимает название первого скрипта (путь до него должен быть полный) и вызывает утилиту dbus-monitor, которая позволяет увидеть, что передается по шине dbus непосредственно для всплывающих уведомлений. Вывод от dbus-monitor мы передаем на вход нашему первому скрипту.

Далее, добавляем в запускаемые приложения GNOME новую команду (у меня выглядит так):
Код:
/home/user/run_speech.sh /home/user/speech.sh

Перезапускаем GNOME и смотрим что получилось. Для проверки можно сделать следующее:
Код:
notify-send "Привет" "Я твое новое голосовое сообщение"


Озвучка обрыва ppp0 в коньках/by goldskif & ratte

Прописываем в коньках (~/.conkyrc):
Код:
${if_empty ${exec /home/user/eth.sh}} $endif

В папке пользователя создаем файл eth.sh:
Код:
#!/bin/sh

LOCKFILE="/tmp/$VAR.lock"
VAR=`sudo ifconfig $1| grep "ppp0"`;
if [ "$VAR" = '' ] ; then
if (! test -f ${LOCKFILE}) then
touch ${LOCKFILE}
echo "человек. обрати внимание, связь потеряна, я нервничаю." | festival --tts --language russian
fi
else
if (test -f ${LOCKFILE}) then
rm ${LOCKFILE}
echo "человек. мы он лайн." | festival --tts --language russian
fi
fi


Озвучка подключения/отключения флешек/by ratte

Снова два скрипта.
Первый - 62-festival.rules положить в /etc/udev/rules.d
Код:
SUBSYSTEMS=="usb", RUN+="/usr/bin/boltalka.sh %k"

SUBSYSTEMS=="block", RUN+="/usr/bin/boltalka.sh %b"
Второй - boltalka.sh положить в /usr/bin например:
(Кликните, чтобы показать/скрыть)
Код:
#!/bin/bash


export PATH=/bin:/sbin:/usr/bin:/usr/sbin

FESTIVAL="festival --tts"

DEVICE=$1
UDEVINFO="udevadm info"

[ -z "$DEVICE" ] && exit
[ -z "$ACTION" ] && exit

function get_device_attr ()
{
path=`find /sys/devices -name $1`
echo `$UDEVINFO --attribute-walk --path=$path | grep $2 -m1 | cut -f 2 -d '"'`
}

function get_device_name ()
{
device=$1

case $device in
[0-9]-[0-9])
s=`get_device_attr $device "product"`
[ -z "$s" ] && echo "device" || echo "$s"
;;
sr*)
echo "optical drive"
;;
[sh]d*)
s=`get_device_attr $device "KERNEL"`
echo "$s drive"
;;
*)
exit
;;
esac

}
function say ()
{
echo "$1 $2" | $FESTIVAL
exit
}

name=`get_device_name $DEVICE`

if [ -n "$name" ]; then
case "$ACTION" in
add)
say "$name" "was found"
;;
remove)
say "device" "has been removed"
;;
change)
say "$name" "was changed"
;;
esac
fi

Комментариев нет: