http://www.microsoft.com/msj/archive/S408.aspx
Dial The Phone With Your PC Email This
Print This
View My Personal Library
PC Operating Instructions
June 1993 • Vol.4 Issue 6 Add To My Personal Library
Dial The Phone With Your PC
Personal computers have certainly created many new abilities--electronic spreadsheets, desktop publishing, online services. Pretty amazing.
But they can also add some dazzle to "low-tech," everyday tasks--like dialing the telephone. That's right, I'm talking about telephone calls in the ordinary sense--voice calls to people, not data calls to online services. (Online services are dial-up services that let a user access stock quotes, encyclopedias and newspaper articles, make travel reservations and go shopping, and talk to other computer users who are connected to the services. To use the services, the computer user must have a modem, a device that lets a computer transfer and receive information over a normal telephone line.)
The telephone is surely the most often-used office tool, and it's definitely handy at home, too. Since it's used so often, if we can make it more efficient, the productivity payoff can save a good chunk of time.
Presented in this article are three remarkably simple but powerful DOS batch files that will improve your telephone efficiency, provided your computer is equipped with a modem. The first will let you dial a phone number with your computer. The second will dial a phone number and store it with the date and your comments to a phone log. And the third batch program will let you search your phone log to find a phone number you've dialed before.
But first, let's back up and take a look at this thing called a batch file. A batch file is a DOS file that contains a series of commands that can be sequentially carried out without user intervention. Batch files have the extension [the letter combination after the period] of .DBF. The running of a batch file is called batch processing. And the writing of a batch file is called batch programming.
Don't be scared if you haven't done any work with batch files before; we'll thoroughly explain each batch file statement as we go along.
But first, let's talk about one simple thing--why would you want to use your PC to dial the phone? First, it could help you avoid dialing errors because you can type the number, check it, then dial it. More importantly, it can store the number for future use. The number along with personal comments can be stored in an easily searchable form, so that later you can look up a phone number from your phone bill to see who was called, or you can look up a name to retrieve the phone number. Since the phone log holds a record of all your calls, it's been unexpectedly useful as a chronological log and research tool.
The telephone is surely the most often-used office tool, and it's definitely handy at home, too. Since it's used so often, if we can make it more efficient, the productivity payoff can save a good chunk of time.
A Batch Program To Dial The Phone
This first batch file (called Phone.bat) is mainly for educational purposes. All it does is dial the phone. It starts out by turning ECHO OFF to keep the display tidy; this tells the system to perform each statement without printing it first on-screen. CLS clears the screen. These two statements begin almost all batch files. Take a look at the program first, then we'll talk about the file in more detail.
echo off
cls
if "%1" == " " goto ERROR
echo DIALING %1
echo ATDT%1; >COM 1:
echo .
echo .
echo .
echo Pick up the handset,
echo When connected, press any key
echo to disconnect the modem.
pause
echo Hanging up now ...
echo ATH >COM1:
goto END
:ERROR
cls
echo ERROR! You must give a phone number.
echo DIAL.BAT dials the phone number you provide.
echo A parameter is required,
echo for example: DIAL 767-1234
:END
The third line uses an IF statement and a GOTO statement to make sure that you've provided a phone number when you start the program. You'll see how this works if you read on.
Let's say you have a batch program named Whereis.bat. You would use the command WHEREIS to start it up. (Just type whereis at the C> prompt.) The pieces of information you include after the WHEREIS command are called arguments. If you start the program with the command WHEREIS FRED TOM JOHN, then FRED is the first argument, TOM is the second argument, and JOHN is the third. In batch-speak, the terms %1, %2, and %3 are used to represent these arguments. The term %1 would be equivalent to FRED, %2 would be equivalent to TOM, and %3 would be equivalent to JOHN.
If you start Phone.bat by typing in the command phone 777-3456 then %1 is equivalent to 777-3456. If you type in the command phone alone to start the program, that is with no argument, then %1 is empty. The IF statement is true, and the program executes the GOTO statement to jump down to the section labeled :ERROR, which displays a message and quits the program. (Note that labels begin with a colon.)
So, you must include a phone number to use this batch program, otherwise you get an error message.
When an argument is included, the GOTO command is skipped. The next line uses an ECHO command to display the number that will be dialed: DIALING 767-1234.
After that, the line echo ATDT%1; >COM1: sends the phone number to your modem. This line is the heart of the program, so we'll explain in detail.
The ECHO command is normally used to display text on the screen. For example, the statement ECHO WARNING would show the word WARNING on the screen. However, we want information sent not to the screen but to the modem. So, the greater-than (>) symbol is used to "redirect" the information to COM1:. Think of it as an arrow, rerouting the results of the command. But wait a minute. COM1:, what's that?
COM1: is the device name for the first serial port, or communications port. Serial ports are receptacles at the back of your computer into which you can plug in various devices including a modem, mouse, scanner, joystick, and some printers. (LPT1: is a similar name for the parallel port that most printers plug into.) A serial port transfers information through a cable one bit at a time. A parallel port, on the other hand, transfers eight bits (one byte) at the same time. (A bit is the smallest piece of data a computer will recognize; a byte consists of eight bits and equals one keyboard character.
NOTE: If your modem is plugged into the second serial port (COM2:), you'll need to change references of COM1: to read COM2: in these batch files. (Mouse users, take note!)
So, the statement
echo ATDT%1; >COM1:
sends the letters ATDT plus the phone number (the first argument) to the first serial port (to which your modem is connected). What's that ATDT business? Those are instructions to the modem to wake up and dial. AT means ATtention in the industry standard Hayes command set. DT means Dial using touch-Tone signals; if you have pulse telephone service, you'll need to change that ATDT to ATDP. (The Hayes command set is the most common set of commands for modem communications.)
If you were to start this program by typing the command
phone 767-1234
the string of characters ATDT767-1234 is sent to the modem, causing it to dial that phone number. (Make sure the modem is turned on!)
A bit further down, the PAUSE command is used to wait until the number is dialed and you have a chance to pick up your telephone. The PAUSE command automatically displays the message "Press any key to continue. . . ."
Once you pick up your handset and press a key, the message "Hanging up now" is shown and the letters ATH are sent to the modem. That means ATtention Hangup. This doesn't disconnect the call, but tells the modem to release the line to your telephone so you can talk.
Well, so much for the educational example. All it does is dial the phone. What we really want to do is dial and store the phone number, along with a few of your comments, for later retrieval.
If you include at least one word to describe the phone call (a comment of up to eight words is allowed), the phone number and comment are stored.
Keeping A Phone Log
Now let's get down to business. The following batch program, called Dial.bat, will dial the phone as well as store the dialed number along with some comments.
You start Dial.bat with commands like this:
dial 777-3456
dial 123-4567 up to eight words of comment
dial 767-1234 what time is it?
dial 979-5879 SFpcUsersGroup 24-hour upcoming events message
To make later retrieval easier, you find it best to type comments, which can be no more than eight words, in lower case. You can stretch this limit by not using the spacebar to separate your words: use-dashes-like-this or underscores_like_this to join your words together.
Now, let's take a look at the file Dial.bat:
echo off
cls
echo.
if "%1" == " " goto ERROR
echo DIALING %1
echo ATDT%1; >COM1:
if "%2" = = " " goto DIAL
ECHO MORE|DATE|FIND "Current" > >C:\PHONE.LOG
ECHO MORE|TIME|FIND "Current" > >C:\PHONE.LOG
echo %1 -- %2 %3 %4 %5 %6 %7 %8 %9 > > C:\PHONE.LOG
echo. > > C:\PHONE.LOG
:DIAL
cls
echo DIALED %1
if "%2" == " " goto HANGUP
echo Logging comments to PHONE.LOG:
echo - %2 %3 %4 %5 %6 %7 %8 %9
if not "%9" == echo Comment may be only 8 words, "%9" is last word recorded.
:HANGUP
echo .
echo .
echo .
echo Pick up the handset,
echo When connected, press any key
echo to disconnect the modem.
pause
echo Hanging up now ...
echo ATH >COM 1:
goto END
:ERROR
echo.
echo ERROR! You must give a phone number with optional comments echo.
echo DIAL.BAT dials the phone number you provide
echo and logs comments of up to 8 words to file C:\PHONE.LOG
echo.
echo A parameter is required:
echo DIAL 767-1234
echo DIAL 767-1234 fred re kern project
:END
This batch program is the same as the first, with a few additions.
On line three you'll see the first addition, the "echo." statement. ECHO plus a period is the easiest way to display a blank line on the screen. It doesn't work with some older versions of DOS, so if you have problems with it, replace the period with a blank space.
Note the addition of the term "%2" on line seven. As we saw before with %1 (the first argument), the second argument is referred to by the term %2. After dialing, this line tests whether you included a comment after the phone number. If not, the number is dialed but not stored, and the program jumps down to the section labeled DIAL to finish up.
If you include at least one word to describe the phone call (a comment of up to eight words is allowed), the phone number and comment are stored. First, though, a clever trick is used to record the date and time in your phone log.
Try an experiment at the DOS prompt; type the word date or time. The DATE and TIME commands show the current date or time and let you enter a new one. You'll need to press the ENTER key to skip past entering a new date or time.
For this to work automatically, we need to trick DOS into adding a press of the ENTER key for us. As noted above, ECHO is used to display text on the screen; each line of text sent to the screen ends, of course, with a carriage return (the ENTER key). The MORE command "chunks" text into screenfuls and interprets an empty ECHO command as adding an ENTER at the end.
The line
ECHO|MORE|DATE|FIND "Current" > >C:\PHONE.LOG
uses pipe symbols (|) to achieve our goal of pressing ENTER at the end of a DATE command. The blank line produced by an empty ECHO command is piped (rerouted) to the MORE command, which
adds an ENTER to it. This ENTER symbol is routed to the DATE command, causing it to display today's date in this form:
Current date is Mon 06-07-1993
Enter new date (mm-dd-yy):
This text is filtered through the DOS FIND command, which selects just the line with the word "Current" in it. The "> >" at the end redirects the line
Current date is Mon 12-07-1992
into a file called Phone.log on your hard disk. (The hard disk, usually labeled C:, is the nonremovable storage system found inside your computer.) Note the use of the two greater-than symbols, (> >); this redirects the output and appends it to the bottom of the Phone.log file. A single greater-than symbol will create a new file each time, destroying the old Phone.log file, if there is one.
After the date and time are recorded in the Phone.log file, the line echo %l -- %2 %3 %4 %5 %6 %7 %8 %9 > >C:\PHONE.LOG stores the phone number and up to eight words in the Phone.log file. (Using the SHIFT command, it is possible to allow longer comments, but let's not complicate things.)
The next line again uses the "echo." statement to add a blank line, this time not to the screen but at the bottom of the Phone.log file to make it easier to read.
The following lines clear the screen and redisplay the phone number dialed. The comment stored to the Phone.log file (if any) is redisplayed, and, if there is an eighth word (%9 is not empty), a warning is issued that any following words were not recorded.
Of course, the beauty of all this is that you can look up any phone number you've previously dialed with Dial.bat.
Looking Up Stored Phone Numbers
Of course, the beauty of all this is that you can look up any phone number you've previously dialed with Dial.bat. Once Dial.bat creates the file Phone.log, you can use several techniques to search through it. Use the search feature of your word processing program, or try the following simple batch file, called Lookup.bat.
Lookup.bat uses the FIND command to search the Phone.log file. It then displays each line (phone number and comment) that contains a matching search pattern. It does not dial the phone number(s) retrieved. You will need to type it in again (starting with the DIAL command) or use another program that can dial phone numbers picked off the screen.
Start Lookup.bat like this:
lookup fred
lookup 408
lookup client22
Now, here's the Lookup.bat program:
echo off
cls
echo LOOKUP.BAT is case sensitive.
echo Use exact UPPER/lower case.
echo Search pattern may be only 1 word.
echo SEARCHING FOR "%1"
FIND "%1" C:\PHONE.LOG
This batch program is both simple and effective. The FIND command comes with DOS, you just need to make sure that Find.com is in your DOS directory and that your path points to it. To see if it does, type path at the DOS prompt. The drive and directories in the current path are listed on-screen.
(To open a file in DOS, your computer must know in which directory the file is located. The path tells DOS which drive to start with and which directories to search through when looking for a file. The PATH command should be located in your Autoexec.bat file, a batch file that your computer probably loads and runs as it starts up. Your Autoexec.bat file should include a PATH command. That command should include path=c:\dos; but will probably include other directories as well.)
Keep in mind that the FIND command is quite literal. It searches for exact matches of upper case and lower case, punctuation, etc. The way this batch program is set up, you may only search for one-word patterns (that is, no spaces or commas). It also works very well on numbers or word fragments.
Making Dialing Easier
Dial.bat can't be used while you're in another program. Since it's a batch file, you must be in DOS to use it. That's quite limiting.
You may want to dial phone numbers from a dBASE database file (dBASE is a database program from Borland International Inc.) or from comments found in a word processing document created in WordPerfect Corp.'s WordPerfect. It would be nice to be able to hit a couple of keys and have the software pick the phone number off the screen, hit another key and have it dial.
There aren't many companies that offer products that will pick up and dial phone numbers off the screen. Two that will do this, though, are Desqview from Quarterdeck Office Systems and Hotline from Smith Micro Software Inc.
There are many other programs that will store your Rolodex and dial the phone numbers from it (but most will not pick up numbers from the screens of other applications). These include Hotline, Sidekick 2 from Borland International and WordPerfect Office from WordPerfect.
You can also find free or inexpensive dialing programs on your local bulletin board systems, which are sort of like online services in that they lets users dial in with their computers and leave messages for other computer users as well as transfer files and programs to (called uploading) and from (called downloading) the services. Several public domain and shareware programs (free or very inexpensive programs that let you try before you buy) that dial are available with names like PhoneBook, Easy Dialer, Desktop, Fastments and List. Get the exact file name by scanning the file list for the word "dial."
If you make many phone calls, you might want to think about equipping your PC to dial for you. Once you have a modem and dialing software, put your Rolodex in the machine. It will change the way you use your telephone!
by Kendall Callas
Sidekick
Borland International Inc.
1800 Green Hills Road
Scotts Valley, CA 95067
(800) 331-0877
Desqview
Quarterdeck Office Systems
150 Pico Blvd.
Santa Monica, CA 90405
(310) 392-9851
Hotline
Smith Micro Software Inc.
51 Columbia
Aliso Viejo, CA 92615
(714) 362-5800
WordPerfect Office
WordPerfect Corp.
1555 N. Technology Way
Orem, UT 84057
(800) 451-5151
Want more information about a topic you found of interest while reading this article? Type a word or phrase that identifies the topic and click "Search" to find relevant articles from within our editorial database.
Enter A Subject (key words or a phrase):
ALL Words (‘digital’ AND ‘photography’)
ANY Words (‘digital’ OR ‘photography’)
Exact Match ('digital photography'- all words MUST appear together)
Home Copyright & Legal Information Privacy Policy Site Map Contact Us Need Site Help?
Copyright © 2006 Sandhills Publishing Company U.S.A. All rights reserved.
Dialing phone numbers
Using the above information, you can write a batch file to auto-dial a phone number or dial directly from the DOS prompt.
The syntax to dial a local phone number (111-2345) through a modem on COM1 is: ECHO ATDT1112345 > COM1.
To dial a long distance number, add the "1" and the area code before the phone number: ECHO ATDT18001112345 > COM1.
Some phone systems require dialing an access number, usually an 8 or a 9, prior to the phone number to get an outside line. You usually have to wait a moment for a dial tone before you begin dialing. To use AT commands in this situation, use one or more commas to separate the access number from the phone number. Each comma represents a 2 second pause. The following example dials tone, then 9, waits 2 seconds for an outside line, then dials the
phone number of 111-2345: ECHO ATDT9,1112345 > COM1.
If the 2 second pause is not adequate to receive a dial tone, you could use more than one comma or you could increase the length of the pause. The 2 second pause is determined by the setting of the S8 register. You can change the contents of that register, if needed, to make the pause longer. To change the default 2 second pause to a 5 second pause: ECHO ATS8=5 > COM1.
The command syntax to disable call-waiting is: ECHO ATDT*70,1112345
Call-waiting is disabled for the scope of the call and terminates when the call is disconnected.
Enabling speaker for the modem
The modem speaker can be shut off completely, overriding the speaker volume settings below. You might want to have the speaker on
until the modem connects to listen for operator messages, etc.
Mx Speaker Control Commands
M0 Speaker off
M1 Speaker on until carrier detected (connected) ** Default
M2 Speaker on continuously
M3 Speaker on after dialing until carrier detect (connected)
Shut the speaker off completely: ECHO ATM0 > COM1
Speaker on until connected: ECHO ATM1 > COM1
Speaker on and leave on: ECHO ATM2 > COM1
Speaker off during dial, on until connect: ECHO ATM3 > COM1
Adjusting speaker volume
If the modem speaker volume is too low, use the L command to adjust the Loudness. Values range from the lowest, L0, to the highest, L3. The default setting is usually L2.
Lx Speaker Volume Commands
L0 Lowest
L1 Low
L2 Medium **Default
L3 Highest
To lower modem speaker volume: ECHO ATL1 > COM1
Maximum speaker volume: ECHO ATL3 > COM1
Combining AT Commands
You could send each command separately or combine. This command dials using tone (DT), medium loudness (L2), the phone number 111-2345 using COM1: ECHO ATL2DT1112345 > COM1.
Other useful AT Commands
Set the modem to auto-answer: ECHO ATS0=1 > COM1
Disable auto-answer: ECHO ATS0=0 > COM1
Execute a software reset: ECHO ATZ > COM1
The following is a simple QBasic program to dial a phone number. This program will prompt for a number to dial. Be sure to include any access numbers required, such as area code, long distance codes, etc.
REM Save as CALL.BAS
CLS
INPUT "Phone number to dial ", PNumber$
OPEN "COM1:" as #1
PRINT #1, "ATDT"; PNumber$
INPUT "Press the ENTER key to hang up ", X$
CLOSE
Remember to change Line 4 to the communications port used by your system and change Line 5 from ATDT to ATDP if you have a pulse phone.
COMDIAL
So much for internals. Now it's time to write a real app. COMDIAL, my sample app, is a Windows 95-based telephone "answering machine" that answers the phone, plays an outgoing message, and gives the caller a chance to record a message (see Figure 6). It also lets you listen to your messages from a remote phone if you enter your password.
To use the new voice modem features provided by Unimodem V, you first have to initialize TAPI, open a logical line device, and negotiate an API version number. (These details are described in the article "Reach Out and Touch Someone's PC: The Windows Telephony API," MSJ, December 1993), which is available on the MSDN CD. I won't repeat them here. In the function telephonyOpen, I store important information about the logical line device and the communication session in a global MYTAPI structure defined in COMDIAL.H.
When you call lineInitialize to initialize TAPI, one of the arguments you pass is the address of a callback function. TAPI calls this function when things happen, like when the phone rings or someone at the other end presses a keypad button. The callback function is where all the action happens. It's the TAPI equivalent of a window procedure and tends to have one of the old mother-of-all switch statements. The callback function has the following signature:
void FAR PASCAL LineCallBackProc(DWORD dwDevice,
DWORD dwMessage, DWORD dwInstance,
DWORD dwParam1, DWORD dwParam2,
DWORD dwParam3)
dwDevice is either the handle of the logical line or the handle of a call in progress, depending on whether the message is line or call related. dwMessage identifies the specific event. dwInstance specifies application-defined instance data to accompany the message. The last three parameters contain details about the event.
As I mentioned earlier, lineGetID provides the link between TAPI and the Wave API. lineGetID returns the device ID of the wave device that corresponds to the phone line. Playing or recording audio with this device ID results in audio being sent or received through the phone line. lineGetID retrieves the ID of the wave device associated with a particular phone line, address, or call.
LONG lineGetID(hLine, dwAddressID, hCall, dwSelect,
lpDeviceID, lpszDeviceClass)
hLine is a handle to an open line device, as returned by lineOpen; dwAddressID specifies an address on the given open line device; and hCall is a handle to a
call. dwSelect is LINECALLSELECT_LINE, LINECALLSELECT_ADDRESS, or LINECALLSELECT_CALL. The lpDeviceID parameter points to a memory location where the device ID is returned. This memory location has the format of a VARSTRING structure.
typedef struct varstring_tag {
DWORD dwTotalSize;
DWORD dwNeededSize;
DWORD dwUsedSize;
DWORD dwStringFormat;
DWORD dwStringSize;
DWORD dwStringOffset;
} VARSTRING, FAR *LPVARSTRING;
This structure is used because lineGetID retrieves IDs of many devices associated with phone lines, not just the wave device. For example, you might want to get the ID of the COM port associated with the modem, which is a string like "COMM/DATAMODEM". Whichever the case, the ID is returned at the end of the VARSTRING struct; its size and offset are specified by dwStringSize and dwStringOffset. The offset is relative to the beginning of the structure. Since the ID of a wave device is a DWORD, the size is four bytes. COMDIAL gets the device ID by copying it from the VARSTRING struct.
memcpy(&waveid,
(LPVOID *)((BYTE *)&varstring + dwStringOffset),
dwStringSize);
COMDIAL allocates space for VARSTRING, plus four bytes for the wave ID DWORD at the end.
VARSTRING *varstring =
calloc (sizeof(VARSTRING)+sizeof(DWORD), 1);
varstring.dwTotalSize =
sizeof(VARSTRING)+sizeof(DWORD);
Don't forget to set dwTotalSize to the amount of memory allocated so Unimodem knows how much memory it has to work with.
The last argument to lineGetID, lpszDeviceClass, tells Unimodem which device ID you want. If you want to play audio over the phone line, you should specify "wave/out"; if you want to record audio, use "wave/in". COMDIAL uses both so it calls lineGetID twice and stores the in/out IDs separately. Most modems use the same ID, but there's nothing in the spec that says they must, so you should store separate IDs for wave in and out.
The function mylineGetWaveID in mytapi_.c shows how to call lineGetID to retrieve a Wave device ID (see Figure 6). There, I call lineGetID twice: first to determine how much memory to allocate, then again to actually retrieve the ID.
Tuning in to Touch Tones
A key feature of voice mail systems is the user's ability to navigate by pressing buttons on the telephone. Each button generates a unique dual tone multi-frequency (DTMF) pair of tones, corresponding to the digits 0 to 9, A to D, * or #. (A to D were included in the original DTMF specification, but do not appear on standard telephones today.) COMDIAL detects and responds to these digits. If I call my answering system and press 1, then enter my password, COMDIAL plays back any messages in my mailbox. A more sophisticated app might manage several different mailboxes for different users of the system. Of course, before you can listen for digits, you have to answer the phone! But I'm going to skip that for the moment and come back to it later.
The initial release of Unimodem did not support digit detection, but Unimodem V supports both detection and generation through two functions: lineMonitorDigits and lineGenerateDigits. As with lineGetID, these functions were always part of TAPI, but they didn't work until now.
lineMonitorDigits has two arguments: hCall, the handle to the call, and dwDigitModes. Use LINEDIGITMODE_DTMF for DTMF digit detection or LINEDIGITMODE_PULSE for pulse digit detection (an older technology still used in some remote areas and foreign countries). Some modems also provide DTMF edge detection; they can detect the down-edge of a DTMF tone, indicating the tone has ended. This is useful if you want to detect digit tone and duration. The flag LINEDIGITMODE_DTMFEND enables down-edge detection if the modem supports it. To disable digit detection entirely, call with dwDigitModes set to 0.
Once you've turned on digit detection, Unimodem sends a LINE_MONITORDIGITS message to your line callback function each time a digit is detected. For a DTMF digit, the dwParam2 parameter of the message is LINEDIGITMODE_ DTMF; for a pulse-mode digit, dwParam2 is LINEDIGITMODE_PULSE. The digit itself is passed in dwParam1. The low byte contains the digit, which will be ASCII 0 to 9, A to D, * or #.
COMDIAL traps these digit events to implement a simple state machine as in Figure 7. Assuming for the moment that COMDIAL has answered a call, played the greeting, and prompted the caller to press either 1 or 2, COMDIAL is in the idle state. Pressing 1 lets me enter a password and hear my messages; pressing 2 lets the caller record a message. If the caller presses 2, COMDIAL goes to rec state, records the caller's message, and goes back to idle.
Figure 7 State Machine for COMDIAL Voice Mailbox
If the caller presses 1, COMDIAL goes to the password 0 state. At that point the caller must enter a password. The example recognizes a single hardwired password: 6727. (Not very customizable, but this is just a demo program.) If the caller enters 6727, COMDIAL goes to playback state, plays the messages in the voicemail box, then returns to idle state. The implementation appears in the line handler function in the switch case for LINE_MONITORDIGITS.
In several places I call playSound to play a message, passing the device ID obtained from lineGetID. For example, to play the "Please enter your password" message, I call
mytapi.h_waveout =playSound (mytapi.dwWaveOutID,"pw.wav", hTTYWnd);
playSound is a helper function that contains standard code for opening and playing wave files. You can find it in SOUND.C, part of COMDIAL. The function takes a wave device ID, the name of the sound file to play, and a handle to the application window for displaying message boxes in case of errors. I use message boxes to display errors because it's easy, but a commercial program should probably spool the errors to a log file or use some other mechanism that doesn't require human intervention, since in general a voice mail app will run unattended.
After the sound file plays, Windows sends a MM_WOM_
DONE message to your main application message loop (see Figure 8). When COMDIAL gets MM_WOM_DONE, I free my buffers and close the wave device. Then, if COMDIAL is in the playback state, I call playSound to play the next message in the voice mailbox. When all the messages have been played, I return to idle state. A more user-friendly app might play a beep or other delimiter between messages, so the caller knows where one ends and the next begins.
COMDIAL doesn't use it, but TAPI has a lineGenerateDigits function to generate (as opposed to monitoring) digits.
LONG lineGenerateDigits(hCall, dwDigitMode, lpszDigits,
dwDuration)
You could use lineGenerateDigits to write a program that talks to other answering systems, so your computer could call my computer and leave a message. The digits are generated in-band, so you will normally call lineGenerateDigits after the call is connected, though this is not a requirement. The dwDigitMode parameter is the same as for lineMonitorDigits, except LINEDIGITMODE_DTMFEND is not supported. You specify the digits in the string lpszDigits. Valid DTMF digits are 0 to 9, A to D, *, and #. A comma adds an extra delay between digits it separates. The delay varies depending on the modem configuration-you can check MinDialParams and MaxDialParams in the LINEDEVCAPS structure filled by lineGetDevCaps for the delay associated with a comma. dwDuration specifies the duration of the digits generated.
Once all the digits are generated-or when digit generation is aborted by calling lineGenerateDigits with a NULL buffer-Unimodem sends a LINE_GENERATE message to your app.
Answering Calls
So far I've shown you how to get the wave device, play sounds and monitor digits, but I skipped over one little detail: answering the phone. A voice mail system isn't
much good if it can't answer the phone. How does COMDIAL do it?
During its initialization, COMDIAL calls lineSetNumRings to set the number of rings it will wait before answering an incoming call. This function is designed to help telephony apps cooperate in implementing "toll-saver" features. When no messages are waiting, COMDIAL picks up after five rings. When messages are waiting, it picks up after three rings. This way, when I call to listen to my messages and the phone rings four times, I know there are no messages waiting. I can hang up before COMDIAL answers and avoid those hefty long distance charges.
The function lineGetNumRings returns the minimum number of rings set by all apps. COMDIAL uses this to respect the toll-saver settings of other apps. Remember: your app may not be the only telephony app running! For example, there could also be a fax program running at the same time. (More on this later.) So when COMDIAL opens the line, it sets the number of rings to for either RINGCNT (five) or the value set by another app, whichever is less. This setting corresponds to the state in which no messages are waiting. After messages are recorded, it sets the number of rings to wait for either RINGCNT1-2 or the value set by another app, whichever is less.
lineSetNumRings doesn't actually do anything except store a TAPI system global variable that apps can share. You still have to answer the phone yourself. Each time the phone rings, Unimodem calls my callback function with a LINE_LINEDEVSTATE message.
case LINE_LINEDEVSTATE:
switch (dwParam1) {
case LINEDEVSTATE_RINGING:
mytapi.nRings = dwParam3;
if (mytapi.bWaitForCall && mytapi.nRings ==
mytapi.nRingCnt) {
// answer incoming calls
lrc = lineAnswer(mytapi.hCall,NULL,0);
if (!(lrc >0 )) {
ProcessTAPIError(lrc);
myMessageBox("Error answering call");
}
mytapi.nRings = 0;
}
break;
}
break;
dwParam1 is LINEDEVSTATE_RINGING and dwParam3 is the ring count. COMDIAL compares the ring count to the number of rings to wait. When they're equal, I call lineAnswer to answer the phone, then reset the number of rings to zero. Note that lineAnswer is an asynchronous function that returns immediately.
If TAPI/Unimodem are able to answer the call successfully, Unimodem notifies my line callback by sending a LINE_CALLSTATE message with dwParam1 set to LINECALLSTATE_CONNECTED. At this point, there is an end-to-end voice connection with the caller. Time to play my greeting, go into IDLE state, and start listening for digits.
case LINE_CALLSTATE:
switch (dwParam1) {
case LINECALLSTATE_CONNECTED:
mytapi.h_waveout =playSound (mytapi.dwWaveOutID,
"greeting.wav",
hTTYWnd);
mytapi.iPlayState = PLAYSTATE_IDLE;
mytapi.iPlaySubState = PLAYSUBSTATE_PASSWORD0;
lrc = lineMonitorDigits(mytapi.hCall,
LINEDIGITMODE_DTMF);
break;
}
New Calls and Handoffs
When COMDIAL is the only telephony app running, it receives a LINE_CALLSTATE message on or before the first ring, with dwParam1 set to LINECALLSTATE_
OFFERING for new calls and dwDevice containing the call handle for the new call. But if another telephony app is running, the other app may answer the call first, and decide not to deal with it. The other app hands the call off so it appears suddenly to your app's callback function as a LINE_CALLSTATE message with dwParam1 containing whatever state the call was in when the other app handed it off, which could be LINECALLSTATE_CONNECTED, LINECALLSTATE_OFFERING, or some other state. Your app never gets any ring indications or other call state messages. Since this can happen, you must be prepared to get a call at any time. For example, you can't rely on always getting a LINECALLSTATE_OFFERING.
You can be certain of one thing: a call handle will always accompany a LINE_CALLSTATE message, whether dwParam1 is LINECALLSTATE_OFFERING or LINECALLSTATE_CONNECTED, or any other LINECALLSTATE_
XXX notification code. Therefore, you should trap the LINE_CALLSTATE message and save the call handle whenever it's a new call-provided you have owner privileges (see Figure 9).
You can only answer and control calls for which you have owner privileges, so you should check dwParam3 for the LINECALLPRIVILEGE_OWNER flag before saving the call handle. (If you call lineAnswer without owner privileges, nothing happens and you get an error.) If the call handle is for a new call, COMDIAL saves it and also calls lineGetID (through a wrapper function, mylineGetWaveID) for both the wave/in and the wave/out devices.
When COMDIAL gets a LINE_CALLSTATE message, it also updates its menus by calling mylineGetCallStatus, which in turns calls the TAPI function lineGetCallStatus to retrieve LINECALLSTATUS information:
typedef struct linecallstatus_tag {
DWORD dwTotalSize;
DWORD dwNeededSize;
DWORD dwUsedSize;
DWORD dwCallState;
DWORD dwCallStateMode;
DWORD dwCallPrivilege;
DWORD dwCallFeatures;
DWORD dwDevSpecificSize;
DWORD dwDevSpecificOffset;
} LINECALLSTATUS, FAR *LPLINECALLSTATUS;
dwCallFeatures is convenient for managing menus. It contains flags that specify which TAPI features are available for the call in its current state. COMDIAL checks that LINECALLFEATURE_ANSWER is set; if not, the Auto Answer menu item is disabled because the call cannot be answered in its current state.
Recording Voice Messages
You can record voice messages from the phone line the same way you record audio from other wave devices. Just open the wave/in device corresponding to the phone line by using waveInOpen with the device ID returned from lineGetID. You must provide a buffer for holding the recorded information and wait for the Wave system to fill it with audio data. The recordMessage function in my example program shows one way to do this.
When recording is complete, Windows sends a MM_WIM_DATA message to COMDIAL's main window procedure. There, I save the recorded data from the supplied buffer, then free the buffer. COMDIAL unceremoniously disconnects the caller after exactly one minute by calling lineDrop (see Figure 10).
In addition to the new voice functionality, Unimodem V has other new features that are not related directly to voice. These include support for call forwarding, logical phone devices, caller ID, G3 fax media, pass-through bearer modes, and flashhook in canonical addresses. Support for the G3 fax media mode is especially significant because, prior to the release of Unimodem V, TAPI apps could not process fax calls. Instead, fax calls were answered and processed through the Messaging API (MAPI). Now TAPI apps can directly make, answer, and process fax calls.
The Operator Agent
Unimodem V comes with a new program called the Operator (see Figure 11). Operator Agent resides in the Accessories folder. When you run it, a telephone icon appears on the Windows 95 system tray. The Operator Agent performs centralized call routing in heterogeneous telephony environments. In plain English, this means the Operator Agent will answer all incoming calls and route them to the appropriate application depending on the type of call: fax, voice mail, or data (terminal and file transfer apps).
Figure 11 "Is this the party to who I am speaking?"
The Operator Agent takes on one of the thorniest problems in computer telephony and does a reasonably good job of solving it. Current modem and fax communication protocols don't take into consideration the possibility of multiple devices sharing a phone line. These protocols assumed the line was used by either a fax machine, a modem, or a human. Before there was line sharing, each type of device was free to implement whatever protocol worked best for that device. Most modems and fax machines implemented a protocol whereby the answering party would send a tone indicating whether it was a modem or fax machine. Since the line was dedicated to either modem or fax, it was safe for each device to assume that only a modem or fax was calling on the line.
Things aren't so simple in heterogeneous environments. It isn't practical for the answering party to send special tones when the caller might be a modem, fax, or a human. Instead, the caller should identify itself as a modem, fax, or human so the call can be routed to the appropriate application. There are several ways of implementing this; each has drawbacks.
First, you can make assumptions about the caller. For example, when the call is first answered respond with a fax tone. If there is no response from a fax machine, try a data modem tone. If there is no response from a data modem, assume the caller is a human and take a message. The disadvantage is that data modems may hang up when they receive the fax tone, and human callers will often hang up when they hear fax and modem tones in their ears.
Another option is to use the telephone ring pattern to determine the nature of the call before answering. For example, when a fax machine is calling, the phone could use a long-long ring pattern; when a modem calls, the phone could use a short-short ring pattern; and when a human calls, the phone could use a long-short pattern. This is an elegant solution because the computer can confidently answer the phone with the correct tone or with no tone if the caller is human. The problem is that modulating the ring pattern (known as distinctive ringing) requires a special phone service from the phone company, and most users currently do not have this service. Also, not all modems support distinctive ring detection.
Finally, you can have the caller identify itself with a touch tone. The answering app must wait for a tone from the caller and respond in the appropriate manner. For example, a tone for digit 1 could indicate that the caller is a fax machine and the answering app could send out a fax tone. The problem with this approach is that there is no standard for what digit corresponds to what type of caller.
None of these solutions is perfect, but the Operator Agent doesn't force you to use any one of them. Instead, it lets you choose whichever scheme works best in your environment.
The Operator Agent may answer calls on behalf of all running telephony applications. It can then either play a greeting (prompting for touch tones to identify the caller as a fax, modem, or human) or route the call immediately according to a selected routing priority. The routing priorities are selected by clicking the Properties button and then the Call Routing Priorities button on the Properties dialog (see Figure 11). The Operator Agent does not get involved in calls identified by a distinctive ring pattern because the type of call is known by Unimodem before the call is answered and Unimodem can route the call to the appropriate app.
Conclusion
With the release of Unimodem V, Windows takes a major step toward becoming an industrial-strength telephony platform. The voice extensions enable an entirely new category of apps: voice mail and answering machines. They also include solutions, albeit imperfect ones, for call routing in heterogeneous telephony environments through support for distinctive ringing and the Operator Agent.
While support for heterogeneous telephony environments has come a long way, there is still room for improvement. Currently, there is no easy way for an app to toggle the media mode of an existing call. A caller might want to end a voice message and send a file to be included with the message. To support this functionality, you have to answer the call in voice mode, record the message, toggle the modem into data mode, receive the file, and attach the file to the voice message. Such functionality would be tricky to implement, especially the part that synchronizes the activities of modems on both ends of the connection as the media mode changes between voice and data. However, computers are good at making the tricky and complex appear simple and elegant. It will be interesting to see what the next release of the Unimodem driver offers.
If you want to start coding with Unimodem V, or you just want to see the full specification, you can download it via the Internet at ftp.microsoft.com in the \developr\TAPI directory.
From the August 1996 issue of Microsoft Systems Journal.
Aucun commentaire:
Enregistrer un commentaire
Votre commentaire constructif ( ou pas ! )