ПАВЕЛ ЗАКЛЯКОВ
Настраиваем автоответчик на базе Linux
У меня зазвонил телефон.
– Кто говорит?
– Слон.
– Откуда?
– От верблюда.
– Что вам надо?
– Шоколада.
К. Чуковский «Телефон»
На самом деле позвонил не слон, а пользователь. И общаться ему пришлось не со мной, а с моим автоответчиком... Да и perl-связи ему не помогли... А вот как работает автоответчик и как его настроить на базе Linux?
Телефонный автоответчик вещь по своей сути очень простая. Обычно, когда к вам поступает входящий звонок, а вас нет или вы не хотите поднимать трубку, то автоответчик ожидает заданное число гудков. Далее поднимает трубку, проигрывает заготовленное сообщение и после переходит в режим записи. Через некоторое время появляетесь вы и прослушиваете поступившие сообщения. Насколько проста эта схема, настолько и проста её реализация с помощью vgetty. При желании схему можно усовершенствовать, добавив немного сервиса. Например, подключив автоответчик к сети Интернет или мобильному терминалу, можно научить его посылать вам уведомления по электронной почте или посредством SMS о том, что появились новые сообщения. Можно даже организовать удобный веб-интерфейс для просмотра/прослушивания принятых сообщений. Чем большим числом функций вы захотите пользоваться, тем больше настроек потребуется. Я нигде не видел грамотного howto или статьи с решением всех возникающих проблем по созданию автоответчика, поэтому решил восполнить этот пробел.
В качестве программного обеспечения используется vgetty, работающая под Linux (настройки проводились под двумя ОС: RedHat Linux v.7.3 и Fedora Core 4). Также мы рассмотрим пример создания примитивного веб-интерфейса. Если вы хотите расширить возможности вашего голосового модема под Linux и свой кругозор, – эта статья для вас.
vgetty (voice getty)
Изначально vgetty разрабатывалась как отдельная программа, работающая с голосовыми функциями модемов. Однако в последнее время, видимо, из соображений разумности она стала входить в состав пакета программ mgetty + sendfax. При этом основная страница этой программы (на которую есть ссылка в man и readme) давно не существует (http://www-internal.alphanet.ch/~schaefer/vgetty.html). Но последнее обновление программы датируется всё же апрелем этого года.
Несмотря на то что в состав вашей операционной системы mgetty+sendfax, скорее всего, уже входит в виде заранее скомпилированного пакета, и его использование на первый взгляд может показаться более простым, мы от этого удобства откажемся. Если, например, у нас что-то не заработает и дело дойдёт до правки исходных текстов, то нам их всё равно придётся установить. Также некоторые программы, работающие с vgetty, например vocp, могут сразу же требовать установку из исходных кодов, а не из готовых пакетов. Поэтому, чтобы не усложнять себе жизнь в будущем, убедимся что mgetty+sendfax как пакет не установлен, запустив команду:
# rpm -qa|grep mgetty
Если в результате нам будут выданы имена установленных пакетов, например, для RedHat 7.3 это могут быть:
mgetty-1.1.30-0.7
mgetty-sendfax-1.1.30-0.7
mgetty-viewfax-1.1.30-0.7
mgetty-voice-1.1.30-0.7
|
или для Fedora Core 4:
mgetty-1.1.33-1
mgetty-sendfax-1.1.33-1
mgetty-viewfax-1.1.33-1
mgetty-voice-1.1.33-1
|
то их следует удалить командой:
# rpm -e mgetty mgetty-sendfax mgetty-viewfax mgetty-voice
Далее зайдём на страницу [3], в разделе «Download Section» найдём ссылки, из которых узнаем адрес FTP-архива – ftp://alpha.greenie.net/pub/mgetty/source/1.1 и номер последней версии. Её и скачаем:
# wget ftp://alpha.greenie.net/pub/mgetty/source/1.1/mgetty1.1.33-Apr10.tar.gz
Далее распакуем архив, например, в директорию /progi командой:
# tar -zxvf mgetty1.1.33-Apr10.tar.gz -C /progi
или
# gunzip -cd mgetty1.1.33-Apr10.tar.gz|tar -xv -C /progi
Покопавшись в дереве файлов распакованного архива и найдя там текстовый файл /progi/mgetty1.1.33/voice/doc/Readme.Beginners, мы можем узнать из него, что для установки vgetty необходимо вначале установить mgetty.
Установка mgetty
Чтобы упростить себе жизнь, меняем текущую директорию:
# cd /progi/mgetty-1.1.33
Привычного файла configure у mgetty нет, поэтому читаем файл README.1st или, не прочитав, запускаем make – получаем ошибку.
You have to create your local policy.h first.
Copy policy.h-dist and edit it.
|
Понимаем, что нам надо создать файл policy.h. Для этого лучше воспользоваться шаблоном policy.h-dist. Копируем шаблон с новым именем policy.h командой:
# cp policy.h-dist policy.h
Далее, просматриваем несложные комментарии перед каждым параметром внутри файла и правим их по необходимости. Возможно, вам покажутся важными другие параметры, но я советую обратить внимание на следующие: DEFAULT_PORTSPEED, MODEM_INIT_STRING, MODEM_CMD_SUFFIX, FAX_IN_OWNER (если вы планируете работать с факсами).
Затем просматриваем Makefile, и если необходимо, вносим правку, например, в переменные путей, куда будет устанавливаться программа. Далее запускаем компиляцию командой make. После, если очень хочется, запустите серию тестов командами make test и make testdisk и посмотрите на их результаты. Полезной функции у этих тестов я не увидел, поэтому советую сразу после компиляции запустить make -n install и по списку напечатанных команд понять, что планируется к выполнению во время установки, а именно, что и куда будет копироваться, какие директории будут создаваться и пр. Увидев среди всех остальных строчку:
# chown fax /usr/local/lib/mgetty+sendfax/faxq-helper
мы можем сделать вывод, что нам надо проверить, и если необходимо – создать учётную запись пользователя, указанного в переменной FAX_OUT_USER в файле Makefile, так как она не создаётся автоматически.
Проверить, есть ли такой пользователь в системе, можно, например, командой:
# cat /etc/passwd|grep fax
Если в результате ничего не выводится, значит, такого пользователя нет, поэтому добавляем его командой:
# adduser fax
Если пользователя в системе не будет, то в процессе установки вы можете получить следующую ошибку:
chown: `fax": неверный пользователь
make: *** [install.bin] Ошибка 1
|
Если вы планируете не только получать факсы, но и отправлять их от имени какого-то конкретного пользователя, то убедитесь, что у него достаточно прав на общение с факс-модемом, посмотрев на права доступа у соответствующего файла-устройства /dev/ttyS? (устройство, к которому будет подключен модем, например /dev/ttyS0):
# ls -l /dev/ttyS0
Если не критично, то проще поставить разрешающие права на чтение и запись для «всех остальных», но обычно так не делают. Когда надо управлять правами доступа к факс-модему нескольких пользователей, то их тогда включают в группу, например, fax (см /etc/group), а на файле меняют группу (см. chgrp) и ставят права для неё (см. chmod). При этом, меняя состав группы fax, можно легко управлять тем, кто может посылать факсы, а кто нет.
Также в целях безопасности я бы подправил запись в файле /etc/passwd, заменив командный интерпретатор для пользователя fax с /bin/bash на /bin/false или на /sbin/nologin. После проделанных действий можно смело запускать установку командой:
# make install
Других ошибок быть не должно. Однако, если они будут, можно вернуться назад и внести необходимую правку. Mgetty установлен, переходим к компиляции и установке vgetty.
Установка vgetty
Если на этапе выше всё прошло успешно, то после смены директории компиляция vgetty должна запуститься без проблем.
# cd /progi/mgetty-1.1.33/voice
# make
Однако, если вы попытаетесь сразу начать компилировать vgetty командой make, пропустив все шаги выше, то вас ждёт сообщение об ошибке:
You didn"t build mgetty first. Please read the documentation. |
Она может также возникнуть, если что-то ранее с mgetty прошло неудачно. В этом случае надо возвращаться назад и внимательно читать все выводимые в процессе сообщения. После завершения компиляции запускаем:
# make -n install
чтобы посмотреть, что куда будет ставиться. Далее, после одобрения выданного, запускаем установку командой:
# make install
После этого считаем vgetty установленным.
Настройка vgetty
Создаём конфигурационный файл voice.conf в той же директории (/usr/local/etc/mgetty+sendfax), где лежат все настроечные файлы mgetty. Проще всего воспользоваться шаблоном voice.conf-dist, который поставляется вместе с дистрибутивом, подправив его по необходимости. Для этого копируем шаблон с новым именем:
# cp /progi/mgetty-1.1.33/voice/voice.conf-dist/usr/local/etc/mgetty+sendfax/voice.conf
и переходим к редактированию voice.conf. На время отладки, то есть пока у вас всё не заработает, советую повысить уровень логирования до 6, указав:
voice_log_level 6
Далее обратите внимание на то, где будут храниться голосовые сообщения в поддиректориях:
voice_dir /var/spool/voice
а также кто окажется их владельцем и с какими правами будут создаваться файлы:
phone_owner root
phone_group phone
phone_mode 0660
Как видим, будет использоваться группа phone. Как вариант её можно сменить на fax, так как такого пользователя мы уже добавили, а можно добавить нового пользователя phone, аналогично тому, как это было сделано выше. При этом при создании учётной записи пользователя будет создана и одноимённая группа. Если в файле группа указана, а реально её не существует, то при создании файла с пришедшим сообщением его группой по умолчанию будет root. В следующей строчке указывается имя файла, который будет создаваться в директории «voice_dir», когда будут появляться новые сообщения.
message_flag_file .flag
Это нам может понадобиться, если мы захотим написать какой-нибудь удобный интерфейс для просмотра файлов. Далее указывается имя поддиректории внутри «voice_dir», куда будут складываться файлы с новыми сообщениями:
receive_dir incoming
Затем указывается имя поддиректории внутри «voice_dir», где будут храниться сообщения голосового приветствия нашего автоответчика:
message_dir messages
Таких сообщений может быть много, и система может выбирать их случайным образом из списка в файлов, который указывается в параметре:
message_list Index
Если случайный поиск по каким-то причинам не срабатывает, например, если мы не будем создавать этого файла и у нас приветственное сообщение будет одно, то следующий параметр определяет конкретное имя файла с приветственным сообщением:
backup_message standard.rmd
Позже мы создадим этот файл. Если требуется, вы можете заготовить несколько сообщений, а далее написать небольшую программку на bash, меняющую эти файлы, например, простым копированием. Если запускать её с помощью crontab (crond)( [2], стр.164-165, [11], стр 34-38), то без собых усилий можно сделать на утро одно сообщение, на время обеда другое, а на вечер третье. В параметре answer_mode оставляем только voice:
answer_mode voice
Небольшое отступление. Если написать:
answer_mode voice:fax
или оставить как есть:
answer_mode voice:fax:data
то во время воспроизведения голосового приветствия, если модем услышит «вызывающие попискивания» на другой стороне, то он тут же прервёт воспроизведение звука и перейдёт в режим приёма факса. После чего если удалённая сторона будет посылать факс, то модем его примет, а vgetty запишет его в формате g3 в директорию /var/spool/fax/incoming под именем вроде ff359168dS0-7-095-1234567.01, где «7-095-1234567» есть fax-id, то есть то, как нам представился удалённый факс. Естественно, на другой стороне записать в fax-id могут что угодно, поэтому не удивляйтесь если когда-нибудь увидите свой номер. Если же faxid не передавался, то имя у файла будет без него, например, что-то вроде одного из следующих: ff353bb0eS2.01, ff353d713S2.01, fn353a4b5S2.01, fn353a4b5S2.02. Два последних факса, как легко догадаться, есть один факс, состоящий из двух страниц. О том, что делать дальше с файлами формата g3, как отправлять факсы, как настроить vgetty на совместную работу автоответчика, факса и dial-in-сервера и другие факсовые возможности, будет изложено в отдельной статье, посвящённой факсовым или расширенным возможностям vgetty/mgetty.
Замечание. Если во время воспроизведения голосового приветствия модем услышит вызывающий сигнал факса, а режим работы у него выбран только как voice, то он прервёт воспроизведение, а отвечать и принимать факс не будет. Как сделать, чтобы воспроизведение не прерывалось, пока мне не известно.
Далее указывается скорость порта, к которому подключен модем:
port_speed 38400
Оставить 38400 или поставить 57600, или 115200 решать вам. У этого параметра есть особенность, на которую я хотел бы обратить внимание. Если вы будете менять значение, то необходимо аналогичную скорость прописать и в файле mgetty.config, и наоборот. То есть нужно проверить, чтобы значения были идентичны. Далее можно подправить значение параметра rings, хотя я этого делать не стал, потому как задам количество звонков до поднятия трубки ниже (через ключи в /etc/inittab). Все остальные параметры я оставил без изменения.
В завершение правки конфигурационного файла voice.conf хотел бы рассказать про «грабли» со скоростью порта и поддержку модема ZyXEL Omni 56k PRO, на которые я наступал в течение нескольких недель пока не разобрался в чём дело. Если у вас такой же модем, прочитайте этот раздел обязательно! Итак...
«Грабли»
После установки, изучая и корректируя voice.conf, я добрался до параметра «скорость порта». Предполагая, что конфигурационные файлы обычно пишут не дураки, а установки по умолчанию выбираются из разумных соображений, я оставил предлагаемое значение 38400 без изменений. Помня, что ZyXEL1496B+ не работает на скорости 115200, и учитывая что Acorp-56EMS (на базе чипсета Rockwell) и ZyXEL U1496B+ сразу после установки заработали как надо, повышать скорость необходимости не было.
Проблема была только в одном: модем ZyXEL Omni56K Pro, поддерживаемый vgetty (исходя из документации), не хотел быть автоответчиком, т.е. работать с голосовыми функциями. Через терминал и в других режимах модем работал. Другой такой же модем тоже отказался работать. Чтобы исключить мысль, что оба модема могут быть сломанными, на отдельном компьютере я установил ZVoice и убедился, что в этой программе голосовые функции работают. Предположение, что проблема в кодеках, и попытка использовать кодеки от U-1496B+ результата не принесли, потому как по документации кодеки этих двух модемов не совместимы между собой. Так что работающий U1496B+ ничем не смог помочь собрату. Пришлось разбираться в работе vgetty. Первое, что в этом случае делается – уровень логирования ставится на максимум (voice_log_level 6) и изучаются log-файлы. Я изучал /var/log/vgetty.ttyS0 и /var/log/vm.log, сравнивая выводимые данные от разных модемов. Было замечено, что различие в определении модемов Acorp-56EMS и ZyXEL U-1496B+ и изменение дальнейшего поведения vgetty происходит после команды «ATI». Связано это с тем, что модемы на неё отвечают по-разному, соответственно «56000» и «1496», а неработающий Omni56K Pro – «1507». Предполагая, что в зависимости от ответа модема в программе должно быть ветвление, я запустил поиск этих ответов в исходных кодах. Естественно «56000» и «1496» были найдены, а «1507» – нет. Далее, я перешёл к поиску по ключевым словам «ZyXEL» и «Omni» и можно сказать, что почти сразу нашёл решение проблемы. Например, были найдены две следующие строчки:
{ati, "1500", NULL, &ZyXEL_Omni56K},
{ati, "1501", NULL, &ZyXEL_Omni56K},
|
в то время как выдаваемое не работающим модемом значение 1507 отсутствовало. Не долго думая, я нашёл все места где встречалось 1500 и 1501, но не было 1507, и, разобравшись в несложном коде на Cи, внёс правку. В результате у меня получилось два следующих патча которые я получил после с помощью команды:
# diff -urN старый_файл новый_файл
--- /progi/mgetty-1.1.33/voice/libvoice/detect.c.old Mon Apr 11 01:24:30 2005
+++ /progi/mgetty-1.1.33/voice/libvoice/detect.c Mon Sep 19 01:34:56 2005
@@ -74,6 +74,7 @@
{ati, "1496", NULL, &ZyXEL_1496},
{ati, "1500", NULL, &ZyXEL_Omni56K},
{ati, "1501", NULL, &ZyXEL_Omni56K},
+ {ati, "1507", NULL, &ZyXEL_Omni56K},
{ati, "247", NULL, &Multitech_2834ZDXv},
{ati, "248", NULL, &Sierra},
{ati, "249", NULL, &Rockwell},
--- /progi/mgetty-1.1.33/faxlib.c.old Sun Nov 14 01:14:31 2004
+++ /progi/mgetty-1.1.33/faxlib.c Mon Sep 19 01:50:29 2005
@@ -627,6 +627,7 @@
break;
case 1500:
case 1501:
+ case 1507:
lprintf( L_MESG, "ZyXEL Omni 56K (Plus) detected" );
modem_type=Mt_class2_0;
mis = mdm_get_idstring( "ATI1", 2, fd );
|
Файл faxlib.c можно было подправить, дописав ещё несколько строк, чтобы при обнаружении модема выдавалось не «ZyXEL Omni 56K (Plus) detected», а более правильное «ZyXEL Omni 56K (PRO) detected», но так как это на работоспособность влиять не должно, делать этого я не стал. Соответственно после внесения правки мне пришлось перекомпилировать mgetty и vgetty и установить их заново. Казалось бы, всё теперь должно работать, но не тут-то было. Модем начал воспроизводить звуки, но делал это с замедлением, а записывать вообще отказывался, записывая вместо звуков какой-то потрескивающий шум.
К решению проблемы даже была подключена техподдержка фирмы ZyXEL через закрытый форум. Помочь в явном виде специалисты не смогли. На всякий случай они переписали несколько программок написанных Yuhang Wu на Cи, объёмом все вместе взятые не более 50 Кб, для работы с голосовыми функциями модема, заверив, что программы рабочие. Я взял оттуда какой-то алгоритм или часть кода, но изучая заголовки файла decomp.c наткнулся на строчку: «Required DTE speed : Above 57600 bps.», которая меня и натолкнула на мысль, что предложенные по умолчанию 38400 (то, с чего я, собственно, начал весь этот рассказ про «грабли») не совсем подходят и надо бы попробовать поставить 57600. Я попробовал и «О, чудо!», оно заработало!
Вывод: не всякое предложенное значение по умолчанию подойдёт. На этом мои (и возможно чьи-то ещё) проблемы с установкой решились. Мы подошли вплотную к запуску программы vgetty.
Запуск vgetty
Не смотря на то что после компиляции vgetty представляет собой запускаемый ELF-файл, запускать его следует из /etc/inittab, прописав там примерно следующую строчку:
S0:345:respawn:/usr/local/sbin/vgetty -n 4 ttyS0
- S0– это уникальный идентификатор строки. Выбирается произвольно [1], может иметь длину 2-4 символа [2]. Так как модем подключен к /dev/ttyS0(СОМ1), то удобнее выбрать в качестве идентификатора S0.
- 345 – уровни выполнения, на которых будет работать наша программа: 3, 4, 5. (Замечание: а вы знаете, что помимо привычных уровней 0-6 ещё существуют a,b,c и s или S уровни? Если нет – см. [2] и man init.).
- respawn – говорит init о необходимости перезапуска vgetty в случае его завершения.
- /usr/local/sbin/vgetty – полное имя программы vgetty. Обратите внимание: путь к файлу может отличаться, скорее так и будет, если вы поставили пакет из rpm.
- Ключ «-n 4» говорит снимать трубку после 4-го звонка.
- ttyS0 – порт, к которому подключен голосовой модем.
Количество гудков не обязательно указывать в качестве параметра в данной строке – его можно указать и внутри конфигурационного файла, но это не всегда удобно, особенно если модемов, как и строчек в /etc/inittab, в вашей системе несколько, а конфигурационный файл vgetty у них один. Поэтому выше, во время правки voice.conf, я не стал изменять значение параметра rings.
После внесения указанной строчки в /etc/inittab, чтобы vgetty стартовал, надо послать команду: «kill -1 1» или «init q». Можно также перезагрузить компьютер, хотя это будет не очень хорошее решение. Если же у вас уже запущена копия vgetty, то есть строчка уже была, а вы просто внесли правку, то эффективнее будет запустить «killall vgetty», так как предыдущие две команды не сработают.
Теперь модем слушает линию, а vgetty слушает порт, на котором висит модем. В случае поступления звонка модем должен сообщать vgetty о том, что звонят. После того как будет пропущено количество звонков, указанное с параметром -n (или записано в конфигурационном файле), vgetty даст команду на снятие трубки. Попутно с этим vgetty оценит, что за модем подключен, выберет нужную библиотеку, поищет файл голосового приветствия или выберет один из нескольких и начнёт его воспроизведение в линию. Далее в зависимости от настройки пискнет в линию и перейдёт в режим записи всего того, что говорит позвонивший абонент (динамик модема при этом молчит), записывая всё им сказанное, например, в файл /var/spool/voice/incoming/v-3631-1129050743.rmd. После того как время, отведённое для записи абоненту, истечёт или модем услышит сигнал отбоя, означающий, что на том конце положили трубку, запись будет прекращена и будет выставлен флаг с датой и временем последнего звонка. Другими словами, будет создан файл /var/spool/voice/.flag нулевой длины. Далее vgetty перейдёт опять в режим ожидания звонков.
Если всё проходит успешно, то уровень логирования в файле voice.conf можно уменьшить, а вот если у вас что-то не работает или работает не так, как хотелось бы, то смотрите лог-файлы /var/log/vgetty.ttyS?.log и /var/log/vm.log. Последний файл ведётся программой vm, входящей в комплект пакета vgetty, о ней сейчас пойдёт речь.
Формат raw modem data (или сокращённо rmd) не такой популярный, как, например, тот же mp3, поэтому большинство из вас должно было уже давно озаботиться следующими вопросами: а что это за формат rmd, в котором записываются все сообщения? Что мне с ним делать? Как записывать свои файлы, как прослушивать уже готовые файлы? Ответы на эти вопросы смотрите во врезке «Как появился формат rmd».
Дополнительные утилиты
Для того чтобы создавать файлы в том или ином rmd-формате, понимаемом только модемом, или, наоборот, переводить данные в привычные звуковые форматы, в комплекте с vgetty поставляется пакет программ pvftools. В него входят конверторы различных форматов, осуществляющих преобразования между форматами. Из всего представленного списка мне понадобились только четыре программы: wavtopvf и pvftormd для конвертирования «туда» и rmdtopvf и pvftowav – «обратно». Помимо конверторов вам ещё может понадобиться программа pvfspeed для изменения скорости, но мне она не понадобилась, потому как я свои wav-файлы записывал сразу с требуемой частотой дискретизации. Поскольку формат wav (Microsoft Waveform Audio, Windows PCM) не является стандартом в среде Linux, то и прямого конвертора из wav в rmd и обратно нет (в составе pvftools). В качестве промежуточного формата используется portable voice format (pvf). Если вам всё же не очень нравится набирать команду конвертирования дважды, то напишите маленький скрипт, который можно назвать wav2rmd или rmd2wav, а он уже будет внутри содержать последовательные вызовы двух конверторов. Почитав документацию (/progi/mgetty-1.1.33/voice/doc/Readme.pvftools) и help к конверторам, вы можете без труда самостоятельно освоить конвертирование. При этом можете перенаправлять вывод одного конвертора на вход другого, что очень удобно. Например, если вы захотите создать файл гостевого приветствия standard.rmd из имеющегося у вас файла message.wav для модема ZyXEL Omni 56k PRO, то это можно сделать следующей командой:
# cat message.wav | wavtopvf | pvfspeed -s 9600 | pvftormd ZyXEL_Omni56K 4 > standard.rmd
В обратную сторону получится даже проще:
# cat v-1329-1127227035.rmd | rmdtopvf | pvftowav > file.wav
При этом если у вас на компьютере окажется настроенная и работающая звуковая карта, то можно проиграть файлы, перенаправив вывод конвертора не в файл, а напрямую на устройство /dev/dsp или /dev/audio, или /dev/mixer.
Ещё в комплект vgetty входит программа vm. Man к ней нет, поэтому информацию о том, как ею пользоваться, можно узнать путём запуска «vm -help» или из документации. Для воспроизведения файлов на модеме используется команда «play», а далее указывается, куда модему следует выводить звук. В зависимости от модема и от значения, указанного в параметре -d, звук может выводиться как в линию, так и на встроенный на модеме динамик. Модем U1496B+ и Acorp56EMS смогли воспроизвести звук и в линию, и на динамик, а вот Omni 56K PRO на динамик играть отказался. Причём, как я понял, читая документацию [6, стр. 100] параметр +VLS не подразумевает выбор встроенного динамика на Omni 56K, в то время как у U1496B+ такая возможность в параметре +VLS была [5, стр 15-16]. Чтобы не гадать и не перепроверять руками все параметры ключа d, что я и делал поначалу, можно запустить команду:
# vm devicetest -l ttyS0
которая выведет результат тестирования модема, а именно куда может быть осуществлён вывод звука при воспроизведении файлов на модеме. Для ZyXEL Omni 56k PRO вывод программы оказался следующим:
Test Dialup Line, Int. Mic. and Int. Speaker: not supported by vm/vgetty-modemdriver
Test Dialup Line, Ext. Mic. and Ext. Speaker: not supported by vm/vgetty-modemdriver
Test Dialup Line and Local Handset: not supported by vm/vgetty-modemdriver
Test Dialup Line and Int. Speaker: not supported by vm/vgetty-modemdriver
Test Dialup Line and Ext. Speaker: not supported by vm/vgetty-modemdriver
Test Local Handset: OK
Test Int. Speaker: not supported by vm/vgetty-modemdriver
Test Ext. Speaker: not supported by vm/vgetty-modemdriver
Test Int. Microphone: not supported by vm/vgetty-modemdriver
Test Ext. Microphone: not supported by vm/vgetty-modemdriver
Test Dialup Line: OK
Test No Device: OK
|
Это как раз совпадает с тем, что я выяснил опытным путём, а именно – звук выводится только двумя командами:
vm play -d 2 -v -l ttyS0 file.rmd
vm play -d 7 -v -l ttyS0 file.rmd
Телефонный аппарат у меня был подключен через модем. В первом случае (-d 2) подключение происходит к текущей линии, а телефонный аппарат от неё не отключается. То есть, если у вас был гудок, то воспроизведение звука будет поверх гудка. При этом оно почему-то завершается раньше. Но вот, если в телефонной линии тишина, скажем если набрать «1», то тогда всё проигрывается от начала и до конца. Во втором случае (-d 7) перед началом воспроизведения происходит отключение телефонного аппарата, подключенного через модем от телефонной линии. То есть если вы подняли трубку, у вас был гудок и вы запустили команду, то модем отключит вас от линии, проиграет сообщение и подключит обратно, при этом удержания телефонной линии не происходит. Остальные параметры в командах:
- -l ttyS0 – говорит vm к какому порту подключен интересующий нас модем;
- -v – говорит включить режим избыточного вывода различной информации;
- file.rmd – воспроизводимый файл.
Помимо воспроизведения у vm есть ещё функция записи и другие.
Грабли с отбоем
В процессе настройки автоответчика я столкнулся с довольно забавной ситуацией: исходники vgetty подправил, модем настроил, проверил, что он проигрывает файлы, всё как положено, но входящие звонки модем или vgetty записывать отказывались.
Что я наблюдал: поступает звонок, модем поднимает трубку, воспроизводит голосовое приветствие, а после вешает трубку не дав ничего сказать. Что же оказалось? Так как никакого звукового файла, который я мог бы использовать, под рукой не оказалось, то я решил просто позвонил сам себе с другого телефона. Так как ничего воспроизводить не надо было (файла standard.rmd просто не было) модем сразу бикнул в линию перейдя в режим записи, а я записал своё приветствие. Далее я просто взял и переписал появившийся после этого файл на место standard.rmd в директорию messages. Так получилось, что по окончании сообщения я положил трубку и модем записал вдобавок к моему сообщению ещё два гудка. Теперь, когда этот файл проигрывался в линию, модем воспроизводил и гудки отбоя. Как вы теперь догадываетесь, вторая часть модема в этот момент не дремала, а чётко отслеживала ситуацию в линии: «a не положил ли звонящий трубку?», естественно, услышав в линии гудки в хвосте моего приветствия, она решала, что коли абонент уже отключился (раз пошли гудки), то и модему надо тоже повесить трубку, что он, собственно, и делал. Естественно, проблему решил удалением последних нескольких секунд с гудками из записи голосового приветствия, после чего всё заработало как надо.
Замечание. Не всегда, но пару раз заметил, что сразу после включения компьютера и модема при поступлении звонка модем может поднять трубку и не воспроизвести голосовое сообщение и не перейти в режим записи. Зависание длится, пока не закончится тайм-аут, либо не будет положена трубка. Видимо, это какой-то инициализационный глюк, потому как после данного сбоя (при последующих звонках) всё работает и ни разу не сбоило.
Веб-интерфейс автоответчика
Просматривать, появилось ли у вас новое сообщение на автоответчике каждый раз с консоли сервера или заходя удалённо на него посредством ssh – не самое лучшее решение. Если компьютер имеет подключение к сети, использующей IP-адреса, то лучше всего организовать взаимодействие с сообщениями автоответчика посредством веб-интерфейса. Для этого лучше всего подойдёт небольшой CGI-скрипт [10], написанный на bash [11]. Предполагается, что у вас уже установлен и запущен веб-сервер apache [12] с поддержкой cgi-скриптов, и вы знаете в какую директорию их следует помещать (обычно /var/www/cgi-bin).
Предупреждение! Все скрипты ниже написаны исключительно в демонстративных целях, чтобы показать вам, насколько просто сделать всё самому и побудить вас написать свою оболочку для работы с голосовыми сообщениями. Защита «от дурака» намеренно не делалась, чтобы не терялась наглядность. С точки зрения безопасности недопустимо использование этих скриптов в открытой сети без дополнительной доработки и защиты! Например, скрипт, удаляющий файлы, может удалять любые файлы в системе, если это позволяют права файлов, то есть можно удалять не только голосовые сообщения. Есть и другие потенциально опасные возможности.
Поскольку наш cgi-скрипт будет запускаться и работать от имени веб-сервера, то есть под пользователем apache, нам следует проследить, чтобы скрипт имел права на чтение и удаление голосовых сообщений. Для этого можно поступить двояко: либо добавить пользователя apache в группу phone в файле /etc/group, либо в /usr/local/etc/mgetty+sendfax/voice.conf указываем:
phone_group apache
phone_mode 0660
также следует убедиться, что права, установленные на директорию /var/spool/voice/incoming также позволяют удалять файлы. Например, можно в качестве группы файла указать apache и для группы установить атрибут «+w».
# chgrp apache incoming
# chmod g+w incoming
Если скрипты, будучи запущенными через веб-интерфейс, не работают, а вам кажется, что вы всё настроили правильно, то лучше всего изменить временно свой идентификатор на apache с помощью команды:
# su --login apache
и уже от имени пользователя apache запустить и посмотреть, что не работает. Единственное замечание, для такой операции в файле /etc/passwd придётся временно прописать пользователю apache командный интерпретатор /bin/bash вместо привычных для него /bin/false или /sbin/nologin.
Те, кто хорошо знакомы с html, cgi и bash, могут сразу перескочить в конец статьи, а со всеми остальными начнём с простого – в директории для скриптов создадим файл spisok.sh:
#!/bin/bash
message_dir="/var/spool/voice/incoming"
echo "Content-type:text/html; charset=koi8-r"
echo
echo "<html><body>"
for f in `ls $message_dir`
do
echo "$f<br>"
done
echo "</body></html>"
присвоим ему атрибут выполняемости командой:
chmod +x spisok.sh
и убедимся, что он выводит список сообщений при обращении к нему. То есть запустим браузер и обратимся по адресу: http://192.168.0.1/cgi-bin/spisok.sh (вместо 192.168.0.1 следует указать адрес вашего сервера).
После того как мы убедились, что скрипт работает, усложним его, добавив к сообщениям дату и вывод даты последнего сообщения.
#!/bin/bash
message_dir="/var/spool/voice/incoming"
echo "Content-type:text/html; charset=koi8-r"
echo
echo "<html><body>"
echo "Последнее сообщение: `date -R -r $message_dir/../.flag`<br>"
echo "У вас сообщения:<br>"
for f in `ls $message_dir`
do
d=`date -R -r $message_dir/$f`
echo "$f "
echo "$d<br>"
done
echo "</body></html>"
Теперь, создадим ещё один вспомогательный скрипт play.wav, который при запуске будет получать имя rmd-файла методом GET [10, стр. 100], далее будет его конвертировать в wav (с помощью уже известных нам утилит rmdtopvf и pvftowav) и будет сам представляться браузеру этим wav-файлом, чтобы браузер мог вызывать какой-нибудь установленный в системе проигрыватель звуковых файлов. Несмотря на то что скрипт будет выполнять много действий, сам он состоит всего лишь из четырёх строчек.
#!/bin/bash
echo "Content-type:audio/wav"
echo
cat /var/spool/voice/incoming/$QUERY_STRING|/usr/local/bin/rmdtopvf|/usr/local/bin/pvftowav
Присвоим скрипту атрибут выполняемости, а также создадим внутри нашего списка ссылки, которые будут запускать этот скрипт, заменив всего лишь одну строчку:
echo "$f "
на:
echo "$f "
Как удалять сообщения? Для этого нам понадобится создать ещё один скрипт delete.sh:
#!/bin/bash
rm /var/spool/voice/incoming/$QUERY_STRING
echo "Сообщение $QUERY_STRING удалено.
Также надо будет сделать ссылку, при щелчке на которую этот файл запускался бы. Для этого в скрипте, выводящем список файлов, после последней изменённой нами строки, добавим ещё одну:
echo "<a href=\"delete.sh?$f\">delete</a> "
Собственно, и всё. по сути больше ничего и не нужно. При желании можно можно объединить файл для удаления и выводящий список в один, чтобы после удаления у пользователя в окне браузера висело не только сообщение о том, что файл был удалён, но и список оставшихся сообщений. А также для привычности восприятия можно вместо расширения rmd выводить расширение wav. В результате получится, что весь наш веб-интерфейс состоит из двух скриптов: play.wav и модифицированного файла списка – нижеследующего файла 1.sh:
#!/bin/bash
message_dir="/var/spool/voice/incoming"
echo "Content-type:text/html; charset=koi8-r"
echo
echo "<html><body>"
if [ "$QUERY_STRING» != "" ]
then
rm $message_dir/$QUERY_STRING
echo "Сообщение $QUERY_STRING удалено.<br>"
fi
echo "Последнее сообщение: `date -R -r $message_dir/../.flag`<br>"
echo "У вас сообщения:<br>"
for f in `ls $message_dir`
do
d=`date -R -r $message_dir/$f`
fn=`echo $f|cut -d"." -f1`
echo "<a href=\"play.wav?$f\">$fn.wav</a> "
echo "<a href=\"`basename $0`?$f\">delete</a> "
echo " $d<br>"
done
echo "</body></html>"
Рисунок 1. Внешний вид браузера после запуска скрипта 1.sh
Рассказать о всех возможных нюансах настройки модемов на нескольких листах статьи явно невозможно, поэтому я постарался изложить от начала и до конца, как сделать базовые функции автоответчика. Мне хочется верить, что каждый из вас, дочитавший эту статью до конца, не только сможет настроить свой автоответчик, но и немного пофантазировав над не хватающей функциональностью и дизайном, сможет сам создать что-то более красивое и удобное!
Приложение
Как появился формат rmd
К моменту массового появления персональных компьютеров, а соответственно и первых модемов, теория цифровой обработки сигналов уже насчитывала порядка полвека, поэтому под действием пользовательского спроса реализация голосовых функций модемов не заставила себя долго ждать. Действительно, чего в этом сложного с современной электронной базой. Берём любую книжку по телефонии того времени и узнаём, что для обеспечения высокой разборчивости речи вполне достаточно передавать сигналы в диапазоне частот 0,3-3,4 КГц (см. рис. 2, красные границы), что чуть уже по спектру того, что может воспроизвести голосом среднестатистический человек. Если взять с запасом справа и слева, то получится полоса аналогового сигнала шириной около 4000 Гц (0-4 КГц). Теперь, если воспользоваться теоремой В.А.Котельникова [9] (Г. Найквиста) об отсчётах, мы можем заключить, что, если будем брать мгновенные значения уровня голосового сигнала с периодом 1/(2*4000), то есть 8000 раз в секунду (см. рис. 3), то по этим значениям при необходимости мы сможем без проблем восстановить сигнал без потерь. Такое преобразование аналогового сигнала к его дискретным отсчётам называется амплитудно-импульсной модуляцией (АИМ). Так как уровень сигнала, взятый в точке отсчёта является аналоговым и фиксированным, то для сохранения или передачи мы можем его квантовать, например, на 256 уровней с помощью аналого-цифрового преобразователя (АЦП) и использовать далее цифровое значение уровня. Опытным путём было установлено, что 256 – есть наилучшее число уровней, так как при меньшем числе теряется качество, увеличиваются шумы квантования, а при большем качество радикально не улучшается. Для передачи одного из 256 значений потребуется ровно 8 бит, так как 28=256. Таким образом, для успешного восстановления сигнала длительностью в 1 секунду нам нужно иметь 8 бит для 8000 отсчётов, то есть всего 64000 бит (64 КБит/c). Если взять обычный моно wav-файл и отбросить у него заголовок, то сигнал, записанный внутри файла, получается примерно таким же способом. Данный способ представления данных называется импульсно кодовой модуляцией (ИКМ, PCM). То есть если бы мы записывали данные из линии «как есть» путём вышеописанной «тупой» оцифровки или в формате wav, то на одну секунду записи потребовалось бы 8 Кб (64 Кбит/8). Нетрудно оценить, что на дискету 5,25” Double Sided/Double Density, характерную для того времени и отформатированную на 360 Кб, влезло бы всего лишь 45 секунд. А на винчестер объёмом 10 Мб влезла бы примерно 21 минута, если предположить, что ничего другого на нём не было записано. При тех безумных ценах хранения единицы информации автоответчик оказался бы золотым, если бы мы захотели сделать ёмкость, адекватную одной стороне 60 или 90 минутной аудиокассеты. Также скорость передачи данных от модема к компьютеру в 64000 бит/c могла показаться очень большой. Каждый модем подключался к компьютеру через последовательный порт, cердцем которого была, и по сей день является, микросхема UART (Universal Asynchronous Receiver/Transmitter) – универсальный асинхронный приёмопередатчик. В IBM PC и PC/XT для этой цели использовалась микросхема типа 8250, которая сама или её аналоги рассчитаны на максимальную скорость 38400 бит/c ([14], стр 250-252). И только для IBM PC/AT было решено применить микросхему 16450, которая превосходила 8250 по скорости. Возьмите для примера вышеупомянутый ZyXEL U-1496B+, который не использует 8250, но и не работает на скорости 115200 [5]. А если взять модемы, которые были раньше, то скорости, на которых они соединялись, поначалу составляли 2400, 1200 и даже меньше бод, для передачи которых совсем не требовалась повышенная скорость порта.
Рисунок 2. Область слухового восприятия. Речевые сигналы и музыка занимают лишь часть этой области [15, стр. 63]
(бетта индекс 0 – порог слышимости, гамма – порог осязания)
Рисунок 3. Отсчёты голосового сигнала
Получается, что либо скорость 64000 была высока для записи звуков из линии, либо скорость последовательных портов мала. Велись работы как в сторону повышения скорости работы микросхем UART и введения в них дополнительных буферов ввода-вывода, так и в сторону сжатия потока данных, то есть уменьшения объёма. Сжатие даже опережало возможности компьютеров. Потому как ещё в 1947 г. учёные из телефонных лабораторий фирмы «Белл» опубликовали первое сообщение о полностью работоспособной системе ИКМ и примерно в то же время Делорен во Франции изобрёл дельта-модуляцию [8]. После появления ИКМ было замечено, что при преобразовании речи соседние отсчёты обычно сильно не отличаются друг от друга и потому вместо одного из 256 значений для отсчёта можно передавать его изменение относительно предыдущего. При этом для передачи изменения при той же сетке квантования требуется меньшее число значений, например, если это будет всего 16 уровней, а не 256, как ранее, то надо передавать 4 бита (24=16). После перемножения получим, что для представления одной секунды звука необходимо уже 32 килобита, то есть в 2 раза меньше, чем было при ИКМ. Это самый простой способ уменьшения потока (объёма), так как при переходе от отсчёта к отсчёту передаётся их разница, которую обычно обозначают греческой буквой дельта, то данный способ кодирования и назвали дельта-модуляцией. Далее появилась адаптивная дельта-модуляция(ADPCM) и другие более хитрые способы кодирования, позволяющие сжимать данные больше и больше без значительного ухудшения качества. Для дальнейшего улучшения качества сжатия речи стали учитываться особенности речеобразования (формантная структура, звонкие и шипящие звуки и пр.), после чего стали появляться различные алгоритмы и основанные на них голосовые кодеки. Некоторые из них до сих пор используются в системах сотовой связи. Как обычно, о едином и универсальном стандарте договориться не смогли, поэтому каждая фирма стремилась реализовать своё «ноу-хау» в собственных устройствах, а мы на сегодняшний день получили модемы не совместимые между собой и работающие с разными кодеками. Информация, записанная в формате кодека каждого конкретного модема как раз и есть формат raw modem data. Как легко догадаться, формат rmd для каждого модема свой и зависит от используемого в модеме кодека. Например, «Модемы серии Omni56K используют 4битовый ADPCM-алгоритм преобразования звука в цифровой вид с частотой дискретизации 9600 отсчётов в секунду» ([6], стр. 89), хотя команда «AT+VSM=?» выдаёт и другие значения возможной частоты дискретизации (4 ;ZyXEL ADPCM ;4Bit ;(7200,8000,9600,11025)). Благо модемы с голосовыми функциями выпускали не все производители, поэтому число используемых кодеков не превышает сорока, и почти все на сегодня используемые поддерживаются vgetty. Запустив команду:
# pvftormd -L
можно увидеть все поддерживаемые алгоритмы, а также названия модемов, в которых они обычно используются.
Чтобы между файлами в формате rmd не было путаницы, в самом начале перед данными указывается тип модема, чьим кодеком закодировано остальное содержимое. То есть если вы в каком-нибудь редакторе посмотрите rmd-файлы, то в самом начале вы увидите строчку «RMD1Rockwell», «RMD1ZyXEL Omni 56K» или аналогичную.
Литература, ссылки:
- Стахнов А. Сетевое администрирование Linux. – СПб.: БХВПетербург, 2004 г. – 59 с.
- Мохаммед Дж. Кабир Red Hat Linux 6 Server: Пер. с англ. – Издательство «Лори», 2001 г. – 44-45 с.
- Mgetty+Sendfax Archive/Documentation Centre – http://alpha.greenie.net/mgetty/index.html.
- ZyXEL Omni 56K modem, mgetty/vgetty and GPL – http://www.advogato.org/article/134.html.
- Руководство для пользователей. Универсальный модем серии U-1496. Документ № 83011501, Версия 2.21 ZyXEL Communications Corporation и АО МКЦ «Вариант».
- Omni 56K. Техническое руководство. Версия 2.01, файл o56k-tr.pdf.
- Беллами Дж. Цифровая телефония: Пер. с англ. – М.: Радио и связь, 1986 г.
- Былянски П., Ингрем Д. Цифровые системы передачи. Пер. с англ./Под ред. А.А.Визеля. – М.: Связь, 1980 г.
- Гитлиц М.В., Лев А.Ю. Теоретические основы многоканальной связи. – М.: Радио и связь, 1985 г.
- Павлов А. CGI-программирование: учебный курс – СПб: Питер, 2000 г.
- Тейнсли Д. Linux и UNIX: программирование в shell. Руководство разработчика. Издательская группа BHV, 2001 г.
- Уэйнрайт П. Apache для профессионалов. Пер. с англ. – М.: Издательство «Лори», 2001 г.
- Браун М., Ханикатт Д. HTML 3.2 в подлиннике: Пер. с. англ. – СПб: BHV-Санкт-Петербург, 2000 г.
- Борзенко А. IBM PC: устройство, ремонт, модернизация. – 2е изд. М.: ТОО фирма «КомпьютерПресс», 1996 г.
- Калинцев Ю. Разборчивость речи в цифровых вокодерах. – М.:Радио и связь, 1991 г.