Welcome to
Steven A. Frare's
GeoCities Web Page

VB-TAPI 3
 Download the App & Code

 

 

Home

VB-TAPI 3  

The VB-TAPI 3 Answering Machine application is written in Microsoft's Visual Basic 6.0.  This is by no means a commercial grade application, or it is merely a sample, it works and accomplished its goal of getting me more familiar with TAPI 3.0.  You MUST be using Windows 2000 or a newer operating systems (e.g. XP), older Win9x, ME or NT 4 operating systems DO NOT have TAPI 3.0.

This program is written as an exercise and is not intended for anything. You may freely use and modify the source code contained in this product for use in your own applications including commercial applications. However, you may not redistribute any part of this archive or in any way derive financial gain from this demo product without the express permission of its author – Steven A. Frare.

Please do NOT redistribute this archive, instead refer people to my website, where the latest version can be downloaded free! http://www.geocities.com/sfrare

No claim whatsoever is made concerning the suitability of this source code product for any particular task. Anyone who decides to use code from this sample does so at their own risk and agrees not to hold the author or anyone connected with this sample responsible for it's success or failure.

Copyright (C) 2002, Steven A. Frare all rights reserved

The application is standalone, no .dll's or setup program.  It simply requires itself (VBTAPI3.exe) a greeting file in its program directory named Greet.wav and it will create a subdirectory called Messages to hold the recordings.

What this sample demonstrates:

  • Enumerating TAPI Addresses while filtering them based on TSP and capabilities
  • Registering / Un-registering for TAPI events
  • Accessing legacy C based TAPI functions
  • Answering / Disconnecting a call
  • Generating and detecting DTMF digits
  • Getting Caller ID information
  • Playing and Recording .wav files through legacy TAPI devices and the desktop using DirectSound8
Enumerating TAPI Addresses while filtering them based on TSP and capabilities

Enumerating TAPI addresses is demonstrated in the dlgSetup.frm code Private Sub FillDeviceList().  All addresses are filtered first to make sure they are not using the Kernel-Mode Device Driver TSP, the NDIS Proxy TSP or the H.323 TSP's.  Then if the address passes that test it is checked to make sure it supports TAPIMEDIATYPE_AUDIO for audio playback and record.  All addresses meeting this criteria are added to a drop down list.  Several other self explanatory settings are also set here.  If you don't understand what a setting is check the older VB-TAPI page, it has a primer on an answering machine.  Choosing Okay in this dialog saves the settings to the registry at:

HKEY_CURRENT_USER\Software\VB and VBA Program Settings\VB-TAPI3\Settings

Registering / Un-registering for TAPI events

Registering and un-registering for TAPI events is done in the cmdOn_Click method in frmMain.frm code.  The 'On' button is a dual state button.  When it reads 'On' it attempts to register for TAPI events, get the DirectSoundDevice GUID's for recording and playback and disable most of the buttons, while changing the 'On' button caption to 'Off'.  It registers for events through a call to the RegisterForNotification sub.  That sub will unregister the address for events if it detects it has already been registered then call the TAPI 3.0 method RegisterCallNotifications method to register for all TAPI events.

Accessing legacy C based TAPI functions

In the afore mentioned cmdOn_Click sub the sample got the DirectSoundDevice GUID's for recording and playback.  To create a DirectSound capture or play back device requires a GUID.  For the default speaker and microphone on the desktop this is easy, vbNullString will use the default devices.  For the legacy TAPI devices it is a bit of a problem, which unfortunately boils down to a bit of a hack to get around the problem.

There is no way that I can find to record to or play from a file using TAPI 3.0 and Visual Basic...  So a way needs to be invented.  The way used in this sample is to use DirectSound8 to do the playing and recording.  The TAPI address can be correlated to the DirectSound device via the legacy device id, which according to MSDN we can get from the TAPI3Lib.ITLegacyCallMediaControl GetID method.  MSDN states that method is for use by automation clients, like VB, to use.  However not only is the method marked hidden, it contains an unsupported type for automation clients as its second parameter and is thus unusable in VB!  So now the hack...  Simply write around One Hundred lines of code to access legacy TAPI functions to get the ID in place of the one line of code that should have worked!  VBTAPI.bas contains those One Hundred some odd lines to get the job done...  The one useful method VBTAPI.bas contains is GetDeviceId which returns the wave device identifier based on the address index and a string that describes the TAPI Device Class (e.g. "wave/out").  So when the answering machine is turning on it calls GetLegacyWaveIDAsDXDeviceGUID which calls the 'hack' method GetDeviceId then does a string compare against the appropriate DirectSound device name.  If the we needed a "wave/out" device we may get a legacy id '2' then DirectSound should have a device whose name is "WaveOut 2".  A direct match, a bit of a hack but it works.  The application does this twice, once for the record device and once for the play device, the GUID's returned by GetLegacyWaveIDAsDXDeviceGUID are stored in form level variables.  Those GUID's are used when playing or recording.

Answering / Disconnecting a call

Since we have already registered for events there is much to do but what for the call notification event.  You will find all the events are delivered to the gobjTapiWithEvents_Event sub.  In particular for new calls we are interested in the TE_CALLNOTIFICATION event.  When we get it we make sure we aren't already on a call as this sample only handles one call at a time, set the caller id labels on the form and add references to the ITCallInfo and ITBasicCallControl objects exposed by the event.  We also attempt to get caller id, explained in more detail next.  Once that is accomplished we expect to get TE_ADDRESS events of type AE_RINGING and when the ring count is greater than or equal to the ring counts put into the setup form a flag is set and a timer enabled to answer the call.  A timer is used so that the TAPI thread is not held up while the call is answered.  You can see the sub tmrUpdate_Timer where this variable is checked and if it is set the AM_Start sub is called which actually answers the phone and plays the greeting.  The state of the playback is also monitored in tmrUpdate_Timer and when it is done we start recording.

Generating and detecting DTMF digits

When the call is answered the application also turns on digit detection.  It is not enough to have just set the flag to include TE_DIGITEVENT when registering for call events, an application must still explicitly turn on digit detection.  So we call:

gobjMediaControl.DetectDigits LINEDIGITMODE_DTMF

Where gobjMediaControl is a reference to an ITLegacyCallMediaControl object that we set when we got the TE_CALLNOTIFICATION event above.  To disable digit detection is the same call except you pass zero as the argument.  To generate digits you pass a string of digits to be generated to the ITLegacyCallMediaControl::generateDigits method and tell the method how you want the digits generated, like this:

gobjMediaControl.GenerateDigits "*", LINEDIGITMODE_DTMF

Both the disabling of digit detection and the generation of digits (in this case digit) can be seen in tmrUpdate_timer these are called just before the application starts to record the call.

The answering machine application detects digits so that a person can call in and get there messages remotely.  When playing back the greeting file every digit is caught in the TE_DIGITEVENT case of the gobjTapiWithEvents_Event sub.  These digits are concatenated and a simple string compare is done to see if the remote code entered matches the remote code that was setup.  If there is a match the greeting is stopped and the messages start to play back.  Now digit detection performs another function, which is to duplicate the three playback controls on the form.  DTMF 1 is for 'Next', DTMF 2 is for 'Repeat' and DTMF 3 is for 'Stop', which will also hang up the call.

Getting Caller ID information

Caller ID caused me no end of grief, so I will explain how it should work and how it works.  First of all there is a small subroutine to get the caller id info called, aptly enough, GetCallerID which take an ITCallInfo object to query for the caller id strings.  You must be subscribed for caller id at your TelCo to get caller id information!

How it should work:

There should be two places where the GetCallerID sub is called, once when the TE_CALLNOTIFICATION event is sent then again if a TE_CALLINFOCHANGE event is detected with a cause of CIC_CALLERID.  This way we should be assured of getting it on the original notification and then getting updates if the information changes, which is likely to happen in the US since the caller id information is set between the first and second rings of a phone call.

How it does work:

The above steps worked fine in C++, in VB with the Unimodem TSP they broke the application.  In VB if the ITCallInfo::CallInfoString method is called at a time when the event TE_CALLINFOCHANGE did not occur the application would never receive further new call information.  That is it would work once then it would just sit there.  Additionally calling the ITCallInfo::CallInfoString during TE_CALLNOTIFICATION made it so that no TE_CALLINFOCHANGE events were ever delivered!  You cannot rely on TE_CALLINFOCHANGE events to occur, you must attempt to get the caller id information during TE_CALLNOTIFICATION as that may be the only time it is available.  This is true with the Dialogic TSP and the H.323 TSP.  In particular with the H.323 TSP since you don't ever get TE_CALLINFOCHANGE or AE_RINGING events.  So to make it work this application calls GetCallerInfo in three places, TE_CALLNOTIFICATION, TE_CALLINFOCHANGE with cause equal to CIC_CALLERID and TE_ADDRESS with cause AE_RINGING.  To get around not getting further new call notifications the application re-registers for notifications after every call.  This bug can easily be reproduced on Win2K and WinXP using the Incoming VB sample included in the platform SDK.  Note that it only happens when using VB with the Unimodem TSP.  C++ works as expected, VB with the Dialogic TSP works as expected.

Playing and Recording .wav files through legacy TAPI devices and the desktop using DirectSound8

Playing a file with DirectSound8 in VB is extremely simple!  About the only thing non-intuitive is to set the DSBUFFERDESC struct to contain the flag DSBCAPS_GLOBALFOCUS as such:

dsBuf.lFlags= DSBCAPS_GLOBALFOCUS

Failure to do that will cause the sound to stop playing should application not have the input focus.  Other than that check the PlayFile sub for the very few lines of of code it takes to play a file!  It simply takes the GUID of the device to play, which is already set up during the cmdOn_Click method discussed above.  For playing the status is polled in tmrUpdate_timer.  This can also be done with events; I wanted to use both ways so I used events for recording and polling for status during playing.  I probably should have done that the other way around since tmrUpdate_Timer got to be a mess after awhile...

Recording is a little more difficult but not too bad.  Most of the recording code is in basStream.bas.  That code comes from a DirectSound7 example streamto, which was broken but only took a little work to fix.  It uses two DirectX8 events.  When event 0 is signaled the sound buffer pointer is at the beginning of the buffer, meaning we just started or we wrapped around.  When event 1 is signaled the sound buffer pointer is at the middle of the buffer.  So for event 1 the application reads one half of the sound buffer, from the beginning to the middle and appends that to a temp file.  When event 0 is signaled, and we haven't just started to record, the application reads the second half of the buffer, from the middle to the end, and appends that data to the temp file.  This circular buffer routine continues until StopRecording is called, the file is put into the proper wave file format, and the DirectX objects are cleaned up.  The application now resets to go ready for the next call.

One note about sound playback and recording, with a Dialogic TSP the sound was skipping, a sure sign of buffer under run, so I needed to adjust the sound buffers for the Dialogic Wave Drivers which can be done through Control Panel.  I adjusted the from 8192 bytes to 1024 bytes.  The Dialogic sound was a bit quiet to me.

That's it!  Not too bad.  The caller id problem with VB and Unimodem is a bit of a draw back, as well as the sound volume with Dialogic wave drivers however I am a bit of a novice with DirectX so that may be fixable.  The obvious step of trying to control the volume is not the solution.

Trouble

Unfortunately if you have any questions I probably can't help.  My test lab isn't that great and my time demands are high.  You can give it a shot if you are really frustrated and e-mail me, but I have gone for greater than six months without checking this e-mail account.

If you don't see any voice cards listed in the setup drop down menu you probably do not have a voice modem, or the voice modem is not setup correctly.  This link to KenGolf.com looks helpful in that case.  Hopefully you read the first couple of sentences of this document and are running under an Operating System that has TAPI 3 (Win2K, WinXP or newer)

Have fun!

Steve

 

Copyright (C) 2002, Steven A. Frare all rights reserved

Hosted by www.Geocities.ws

1