Vgetty(3pm)
πΈ NAME πΈ
okay so this is Vgetty(3pm) β User Contributed Perl Documentation, bestie. The module is Modem::Vgetty, and it's the interface to vgetty(8). That's the whole name situation. π
β¨ SYNOPSIS β¨
Here's the whole call vibe, no cap:
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;
That's a lot of methods, but you got this. π«Ά
π DESCRIPTION π
Modem::Vgetty is an encapsulation object β yes, it's giving wrapper energy β for writing applications for voice modems using the vgetty(8) or vm(8) package. With this module you can write answering machines and sofisticated voice applications (their word, not mine, but iconic). That's the whole point, periodt. π
π OVERVIEW π
A voice modem is a special kind of modem, bestie. Besides the normal data and/or fax mode, it can also communicate in voice mode. That means it can record sounds it hears from the phone line to the file, play back recorded files, beep to the line, and detect various standard sounds coming from the line β busy tone, silence, dual tone modulation frequency (DTMF) keypad tones, etc. Examples of voice modems include the ZyXEL U1496, US Robotics Sportster (not Courier), etc. π
To use this software with the voice modem, you need the vgetty(8) package installed. Vgetty is distributed as a part of the 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 the 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. The 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 the 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. π
π₯ QUICK START π₯
Here's the starter pack, bestie:
#!/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. π
π METHODS
β¨ Begin and end of communication β¨
Okay so bestie, here's the tea: your Modem::Vgetty object is gonna set up the communication pipes to vgetty the moment it's born β like, right there in the constructor. No waiting around. But when you're done talking to it, you close things off with the shutdown method:
$v->shutdown;
And honestly? The module is a total sweetie and will call this method itself from the destructor if you don't call it explicitly. So it's got your back either way π«Ά
π Low-level communication
Real talk: you probably do not wanna touch these methods directly. They're giving "behind the scenes" energy. Use the higher-level functions instead, mkay? π
receiveβ This one hands you a string that came in fromvgetty. It parses that string looking for the event types and runs the appropriate event handlers. And if an event handler actually gets run? It goes back and waits for another string. Obsessed with the diligence πsend($string)β Sends the string$stringto thevgettyprocess. That's it, that's the whole job.expect($string1, $string2, ...)β Receives a string fromvgetty(using thereceivemethod up above, obvs) and returns it iff it's equal to one of the strings in the argument list. When something different shows up? This method just returnsundef. Not today, wrong string πwaitfor($string)β Waits until the string$sringis received fromvgetty(using thereceivemethod described above). =item chat($expect1, $sent1, $expect2, $sent2, ...)chat($expect1, $sent1, $expect2, $sent2, ...)β This is a chat-script withvgetty. The arguments get interpreted as received-sent string pairs. And here's the cute part: a received string equal to the empty string means noreceivemethod will be called at that spot. So you can build chat scripts that start with the sent string instead of the received one. Iconic little trick π
π Vgetty control methods
These are the miscellaneous methods for controlling vgetty and querying its status. The little admin squad, if you will.
gettyβ Returns the name of the modem special file (e.g./dev/ttyC4). Like a little name tag for your modem π·οΈdevice($name)β Sets the port of the voice modem that input and output is done to. The possible values areqw(NO_DEVICE DIALUP_LINE EXTERNAL_MICROPHONE INTERNAL_SPEAKER LOCAL_HANDSET). Pick your vibe β¨autostop($bool)β With autostop on, the voicelib will automatically abort a play in progress and returnREADY. This is so useful for faster reaction times for voice menus, fr fr. Possible arguments areqw(ON OFF). Note: the interface should probably be changed to accept the Perl boolean arguments (undef, something else). Returns a defined value on success,undefon failure. Not me pointing out the interface might get a glow-up πmodem_typeβ Sovgettycurrently has no way of telling the voice shell the type of the current modem. This method is a proposed interface for figuring out this type. Currently returnsundef. The appropriate low-level interface has to be implemented invgettyfirst. It's giving "coming soon" energy, bestie π
π Voice commands
beep($freq, $len)β Sends a beep through the chosen device using the given frequency (HZ) and length (in miliseconds). Returns a defined value on success orundefon failure. The state of thevgettychanges to"BEEPING"andvgettyreturns"READY"after a beep is finshed. Example:
$v->beep(50,10);
# Possibly do something else
$v->waitfor('READY');
dial($number)β The modem tries to dial a given number. Thevgettychanges its state to"DIALING"and returns"READY"after the dialing is finished. Ring ring, bestie πplay($filename)β Thevgettytries to play the given file as a raw modem data. See the"Voice data"section for details on creating the raw modem data file. It changes the state to"PLAYING"and returns"READY"after playing the whole file. A whole performance, honestly πΈrecord($filename)β Thevgettyrecords the voice it can hear on the line to the given file. It uses the raw modem data format (which can be re-played using theplaysubroutine).vgettychanges its state to"RECORDING"and you need to manually stop the recording using thestopmethod after some time (or, you can setautostopand wait for any event β silence, busy tone, etc). Don't forget to stop it, bestie, it'll just keep going πwait($seconds)β The modem waits for a given number of seconds. Changes its state to"WAITING"and returns"READY"after the wait is finished. Example:
$v->wait(5);
$v->waitfor('READY');
stopβ Thevgettystops anything it is currently doing and returns to the command state. You must usestopwhen you want to call anotherbeep,dial,play,recordorwaitbefore the previous one is finished. Thevgettyreturns"READY"after thestopis called. So it is possible to interrupt a main routine waiting for"READY"from the event handler:
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. Main character energy, the way it can get interrupted by literally anything π«Ά
play_and_wait($file)β It is an abbreviation for the following:
$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. Saving you keystrokes, you're welcome π
π Event handler methods
add_handler($event, $handler_name, $handler)β Installs a call-back routine$handlerfor the event type$event. The call-back routine is called with three arguments. The first one is theModem::Vgettyobject itself, the second one is the event name and the third one is optional event argument. The$handler_nameargument can be anything. It is used when you want to delete this handler for identificating it. Name your children wisely πdel_handler($event, $handler_name)β This method deletes the handler$handler_namefor the$eventevent. The result of unregistering the handler from the event handler of the same event is unspecified. It may or may not be called. So likeβ¦ don't count on it either way, bestie πenable_eventsβ Tells thevgettythat the voice shell is willing to dispatch events. No events are sent byvgettyuntil this method is called. So this is your consent form, basically β¨disable_eventsβ Tells thevgettythat the voice shell doesn't want to receive any events anymore. Boundaries! We love to see it π«Ά
π¦ The readnum method
readnum($message, $tmout, $repeat)β The applications often need to read the multi-digit number via the DTMF tones. This routine plays the$messageto the voice object and then waits for the sequence of the DTMF keys finished by the#key. If no key is pressed for$tmoutof seconds, it re-plays the message again. It returns failure if no key is pressed after the message is played$repeat-th time. It returns a string (a sequence of DTMF tones0-9,A-Dand*) without the final#. When some DTMF tones are received and no terminating#or other tone is received for$tmoutseconds, the routine returns the string it currently has without waiting for the final#. DTMF tones are accepted even at the time the$messageis played. When the DTMF tone is received, the playing of the$messageis (with some latency, of course) stopped.
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. Lowkey a work in progress, don't get too attached π
β EVENTS
π Introduction
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). It's a whole group chat situation, everybody gets pinged π£
πΈ Event types
At this time, the Modem::Vgetty module recognizes the following event types (description is mostly re-typed from the vgetty documentation):
BONG_TONEβ The modem detected a bong tone on the line. ποΈBUSY_TONEβ The modem detected busy tone on the line (when dialing to the busy number or when caller finished the call).CALL_WAITINGβ Defined in IS-101 (I think it is when the line receives another call-in when some call is already in progress. -Yenya). Not Yenya being unsure in the actual docs, we love an honest icon πDIAL_TONEβ The modem detected dial tone on the line.DATA_CALLING_TONEβ The modem detected data calling tone on the line.DATA_OR_FAX_DETECTEDβ The modem detected data or fax calling tones on the line.FAX_CALLING_TONEβ The modem detected fax calling tone on the line.HANDSET_ON_HOOKβ Locally connected handset went on hook.HANDSET_OFF_HOOKβ Locally connected handset went off hook.LOOP_BREAKβ Defined in IS-101.LOOP_POLARITY_CHANGEβ Defined in IS-101.NO_ANSWERβ After dialing the modem didn't detect answer for the time give indial_timeoutinvoice.conf.NO_DIAL_TONEβ The modem didn't detect dial tone (make sure your modem is connected properly to your telephone company's line, or check theATXcommand if dial tone in your system differs from the standard).NO_VOICE_ENERGYβ It means that the modem detected voice energy at the beginning of the session, but after that there was a period of some time of silence (the actual time can be set using therec_silence_lenandrec_silence_tresholdparameters invoice.conf). The silence era πRING_DETECTEDβ The modem detected an incoming ring.RINGBACK_DETECTEDβ The modem detected a ringback condition on the line.RECEIVE_DTMFβ The modem detected a dtmf code. The actual code value (one of0-9,*,#,A-D) is given to the event handler as the third argument. Yesss the third argument finally gets used, we love payoff β¨SILENCE_DETECTEDβ The modem detected that there was no voice energy at the beginning of the session and after some time of silence (the actual time can be set using therec_silence_lenandrec_silence_tresholdparameters invoice.conf).SIT_TONEβ Defined in IS-101.TDD_DETECTEDβ Defined in IS-101.VOICE_DETECTEDβ The modem detected a voice signal on the line. IS-101 does not define, how the modem makes this decision, so be careful. Not the spec being vague, love that for us πUNKNOWN_EVENTβ None of the above :) π¦
π΅ VOICE DATA
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. Every modem doing its own thing, truly individual β¨
β¨ EXAMPLES β¨
π Answering machine
A lil' answering machine? Yeah, it can look exactly like this, bestie:
#!/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;
Go peep examples/answering_machine.pl in the source distribution β it's a way more configurable version of the code above, fr fr. Here's the tea on what it does, step by step:
- First it sets the event handlers for the busy tone case (aka the caller hangs up π« ) or the silence case (aka the caller just... doesn't speak at all π).
- The handler stops vgetty from anything it's currently doing and sets the
$finishvariable to1. - Then reception of the events gets enabled and the welcome message is played.
- Then the answering machine beeps and starts recording the message.
Heads up bestie: we need to check the $finish variable before we start recording, so we can tell whether the user hung up the phone. π
Now we find the first filename <number>.rmd such that this file does not exist, and we start recording the message into that file. We record until the user hangs up the phone or until the timeout hits. Periodt.
π¦ Readnum routine
An interesting application of the low-level routines is the Voice::Modem::readnum method. Its calling sequence has been discussed above (iykyk). Here we're gonna dig into the source code for this routine and its co-routine, so you can write your own variants of readnum β which, ngl, doesn't actually have too general an interface. Also go look at 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, plus a way for that handler to communicate with the 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 that both readnum and the event handler should wait for the next keypress. And $_readnum_in_timeout is a flag the event handler uses to notify the main readnum routine that it forced vgetty to emit the READY message, because 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 the $input value. The actual DTMF key lives 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. That lets us immediately interrupt the wait via the $self-stop> (which emits the READY message). So when the # DTMF tone comes in, we send a stop to vgetty. If something else is received, we stop 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;
}
So the readnum routine just sets up the event handler, then plays the $message and waits for the input (possibly several times, no biggie). The main work happens inside the event handler. At the end the handler gets unregistered and the final value is returned. π«Ά
πΈ Callme script
In the examples subdirectory of the source distribution there's a callme.pl script. It dials the given number and plays the given message. Run it like this:
vm shell -S /usr/bin/perl callme.pl <number> <message>.rmd
π BUGS
There may be some, but honestly it's more likely to be in 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. π
π TODO
Modem type recognition
The vgetty should be able to tell the voice shell the name of the current modem type. π
The _wait() routines
I need to implement routines similar to play_and_wait for other vgetty states as well.
Debugging information
The module currently has some support for writing debug logs β use the $Modem::Vgetty::testing = 1 and watch the /var/log/voicelog file. This needs to be re-done using (I think) Sys::Syslog. I need to implement some kind of log-levels, etc.
Mgetty/Vgetty 1.1.17
Need to figure out what's new in 1.1.17 (I use 1.1.14 now, lowkey behind ngl). I think the new vgetty can play more than one file in the single PLAY command, and it (I think) has some support for sending voice data from/to the voice shell via the pipe, etc.
π AUTHOR
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. π«Ά The 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
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. π
And that's a wrap, bestie! This page is perl v5.34.0, dated 2022-05-12 β go forth and make your modem sing. π
β¨πΈ
π¦ where this page comes from
- debian bookworm: libmodem-vgetty-perl 0.03-4
- debian trixie: libmodem-vgetty-perl 0.03-4
- ubuntu noble: libmodem-vgetty-perl 0.03-4
- ubuntu questing: libmodem-vgetty-perl 0.03-4
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms