Vgetty(3pm) | User Contributed Perl Documentation | Vgetty(3pm) |
Modem::Vgetty - interface to vgetty(8)
use Modem::Vgetty; $v = new Modem::Vgetty; $string = $v->receive; $v->send($string); $string = $v->expect($str1, $str2, ...); $v->waitfor($string); $rv = $v->chat($expect1, $send1, $expect2, $send2, ...); $ttyname = $v->getty; $rv = $v->device($dev_type); $rv = $v->autostop($bool); $rv = $v->modem_type; # !!! see the docs below. $rv = $v->beep($freq, $len); $rv = $v->dial($number); $rv = $v->play($filename); $rv = $v->record($filename); $rv = $v->wait($seconds); $rv = $v->play_and_wait($filename); $v->stop; $v->add_handler($event, $handler_name, $handler); $v->del_handler($event, $handler_name); $v->enable_events; $v->disable_events; $number = $v->readnum($message, $tmout, $repeat); $v->shutdown;
"Modem::Vgetty" is an encapsulation object for writing applications for voice modems using the vgetty(8) or vm(8) package. The answering machines and sofisticated voice applications can be written using this module.
Voice modem is a special kind of modem, which (besides the normal data and/or fax mode) can communicate also in voice mode. It means it can record sounds it hears from the phone line to the file, Play-back recorded files, it can beep to the line, and it can detect various standard sounds coming from the line (busy tone, silence, dual tone modulation frequency (DTMF) keypad tones, etc). An example of the voice modem can be the ZyXEL U1496, US Robotics Sportster (not Courier), etc.
To use this software with the voice modem you need to have the vgetty(8) package installed. Vgetty is distributed as a part of mgetty package. In fact, vgetty is a mgetty(8) with the voice extensions. Vgetty has some support for scripting - when it receives an incoming call, it runs a voice shell (it is program specified in the voice.conf file) as its child process, establishes the read and write pipes to it, and tells it the number of the appropriate descriptors in the environment variables. Voice shell can now communicate with vgetty. It can tell vgetty "Play this file", or "Record anything you hear to that file", or "Notify me when user hangs up", etc. Sophisticated voice systems and answering machines can be build on top of vgetty.
mgetty (including the vgetty) is available at the following URL:
ftp://alpha.greenie.net/pub/mgetty/
Originally there was a (Bourne) shell interface to vgetty only. The Modem::Vgetty module allows user to write the voice shell in Perl. The typical use is to write a script and point the vgetty to it (in voice.conf file). The script will be run when somebody calls in. Another use is running voice shell from the vm(8) program, which can for example dial somewhere and say something.
#!/usr/bin/perl use Modem::Vgetty; my $v = new Modem::Vgetty; $v->add_handler('BUSY_TONE', 'endh', sub { $v->stop; exit(0); }); local $SIG{ALRM} = sub { $v->stop; }; $v->enable_events; $v->record('/tmp/hello.rmd'); alarm(20); $v->waitfor('READY'); $v->shutdown;
The above example installs the simple `exit now'-style handler for the BUSY_TONE event (which is sent by vgetty when user hangs up) and then records the hello.rmd file. Put this text into a file and then point vgetty to it in the voice.conf. After you dial into your voice modem, you can record a 20-seconds of some message. Verify that /tmp/hello.rmd exists. Now delete the line contaning the word "record" and two subsequent lines and insert to the file the following line instead of them:
$v->play_and_wait('/tmp/hello.rmd');
Now call the voice modem and listen to the sounds you have just recorded.
The Modem::Vgetty object will initialize the communication pipes to the vgetty at the creation time - in the constructor. The closing of the communication is done via the shutdown method:
$v->shutdown;
The module will call this method itself from the destructor, if you do not call it explicitly.
Users probably don't want to use these methods directly. Use the higher-level functions instead.
A chat-script with vgetty. Arguments are interpreted as the received-sent string pairs. A received string equals to the empty string means that no receive method will be called at that place. This can be used for constructing chat scripts beginning with the sent string instead of the received one.
There are miscellaneous methods for controllig vgetty and querying its status.
$v->beep(50,10); # Possibly do something else $v->waitfor('READY');
$v->wait(5); $v->waitfor('READY');
my $dtmf; $v->add_handler('RECEIVED_DTMF', 'readnum', sub { my $self=shift; $self->stop; $dtmf = $_[2]; }); $v->enable_events; $v->wait(10); $v->waitfor('READY');
In the previous example the waitfor method can be finished either by the 10-second timeout expired, or by the 'READY' generated by the stop in the event handler. See also the Events section.
$v->play($file); $v->waitfor('READY');
It is repeated so much time in the voice applications so I have decided to make a special routine for it. I may add the similar routines for dial, record, beep and even wait in the future releases.
NOTE: The interface of this routine can be changed in future releases, because I am not (yet) decided whether the current interface is the best one. See also the EXAMPLES section where the source code of this routine (and its co-routine) is discussed.
Events are asynchronous messages sent by vgetty to the voice shell. The Modem::Vgetty module dispatches events itself in the receive method. User can register any number of handlers for each event. When an event arrives, all handlers for that event are called (in no specified order).
At this time, the Modem::Vgetty module recognizes the following event types (description is mostly re-typed from the vgetty documentation):
Voice shell can send the voice data to the modem using the play method and record them using the record method. The ".rmd" extension (Raw Modem Data) is usually used for these files. The ".rmd" is not a single format - every modem has its own format (sampling frequency, data bit depth, etc). There is a pvftools package for converting the sound files (it is a set of filters similar to the netpbm for image files). The pvftormd(1) filter can be used to create the RMD files for all known types of modems.
A simple answering machine can look like this:
#!/usr/bin/perl use Modem::Vgetty; my $voicemaster = 'root@localhost'; my $tmout = 30; my $finish = 0; my $v = new Modem::Vgetty; $v->add_handler('BUSY_TONE', 'finish', sub { $v->stop; $finish=1; }); $v->add_handler('SILENCE_DETECTED', 'finish', sub { $v->stop; $finish=1; }); local $SIG{ALRM} = sub { $v->stop; }; $v->enable_events; $v->play_and_wait('/path/welcome.rmd'); $v->beep(100,10); $v->waitfor('READY'); if ($finish == 0) { my $num = 0; $num++ while(-r "/path/$num.rmd"); $v->record("/path/$num.rmd"); alarm $tmout; $v->waitfor('READY'); } system "echo 'Play with rmdtopvf /path/$num.rmd|pvftoau >/dev/audio'" . " | mail -s 'New voice message' $voicemaster"; exit 0;
See the examples/answering_machine.pl in the source distribution, which contains a more configurable version of the above text. It first sets the event handlers for the case of busy tone (the caller hangs up) or silence (the caller doesn't speak at all). The handler stops vgetty from anything it is currently doing and sets the $finish variable to 1. Then the reception of the events is enabled and the welcome message is played. Then the answering machine beeps and starts to record the message. Note that we need to check the $finish variable before we start recording to determine if user hanged up the phone. Now we find the first filename <number>.rmd such that this file does not exist and we start to record the message to this file. We record until user hangs up the phone or until the timeout occurs.
An interesting application of the low-level routines is the Voice::Modem::readnum method. The calling sequence of this method has been discussed above. The source code for this routine and its co-routine will be discussed here, so that you can write your own variants of readnum (which in fact does not have too general interface). See also the source code of Vgetty.pm for the readnum source.
The readnum routine needs to have its own event handler for the RECEIVED_DTMF event and the way the handler can communicate with this routine. In our solution we use "static" variables:
my $_readnum_number = ''; my $_readnum_timeout = 10; my $_readnum_in_timeout = 1;
The event handler will add the new character to the end of the $_readnum_number variable. The $_readnum_timeout is the number of seconds both readnum and the event handler should wait for the next keypress, and the $_readnum_in_timeout is a flag used by the event handler for notifying the main readnum routine that it forced the vgetty to emit the `READY' message because of the final `#' has been received.
sub _readnum_event { my $self = shift; my $input = shift; # Unused. Should be 'RECEIVED_DTMF'. my $dtmf = shift; if ($dtmf eq '#') { # Stop the reading now. $_readnum_in_timeout = 0; $self->stop; $self->{LOG}->print("_readnum_event(): Got #; stopping\n"); return; } $_readnum_number .= $dtmf; $self->stop; $self->expect('READY'); # Restart the wait again. $_readnum_in_timeout = 1; $self->wait($_readnum_timeout); }
The event handler is installed for the `RECEIVED_DTMF' event only, so it doesn't need to check for the $input value. The actual DTMF key is in the third parameter, $dtmf. Note that the handler will be called when vgetty is PLAYING or WAITING and the readnum routine will be waiting for the `READY' message. This allows us to immediately interrupt waiting by the $self-stop> (which emits the `READY' message). So when the `#' DTMF tone is received, we send a stop to vgetty. If something else is received, we stop the vgetty too but we enter a new wait using $self-wait>.
sub readnum { my $self = shift; my $message = shift; my $timeout = shift; my $times = shift; $_readnum_number = ''; $_readnum_in_timeout = 1; $_readnum_timeout = $timeout if $timeout != 0; $times = 3 if $times == 0; # Install the handler. $self->add_handler('RECEIVED_DTMF', 'readnum', \&_readnum_event); while($_readnum_in_timeout != 0 && $_readnum_number eq '' && $times-- > 0) { $self->play_and_wait($message); last if $_readnum_in_timeout == 0; while ($_readnum_in_timeout != 0) { $self->wait($_readnum_timeout); $self->expect('READY'); } } return undef if $times < 0; $self->del_handler('RECEIVED_DTMF', 'readnum'); $self->stop; $self->expect('READY'); $_readnum_number; }
The readnum routine just sets up the event handler, then plays the $message and waits for the input (possibly several times). The main work is done in the event handler. At the end the handler is unregistered and the final value is returned.
In the examples subdirectory of the source distribution there is a callme.pl script. This dials the given number and plays the given message. Use the following command to run it:
vm shell -S /usr/bin/perl callme.pl <number> <message>.rmd
There may be some, but it will more likely be in the vgetty itself. On the other hand, there can be typos in this manual (English is not my native language) or some parts of the interface that should be redesigned. Feel free to mail any comments on this module to me.
The Modem::Vgetty package was written by Jan "Yenya" Kasprzak <kas@fi.muni.cz>. Feel free to mail me any suggestions etc. on this module. Module itself is available from CPAN, but be sure to check the following address, where the development versions can be found:
http://www.fi.muni.cz/~kas/vgetty/
Copyright (c) 1998 Jan "Yenya" Kasprzak <kas@fi.muni.cz>. All rights reserved. This package is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
2022-05-12 | perl v5.34.0 |