Chapter 17

SAPI Tools-Using SAPI Objects with Visual Basic 4.0


CONTENTS


The Microsoft Speech SDK contains a set of OLE library files for implementing SAPI services using Visual Basic and other VBA-compatible languages. In this chapter, you'll learn how to use the objects, methods, and properties in the OLE library to add speech recognition (SR) and text-to-speech (TTS) services to your Windows applications.

There are two OLE libraries:

Note
The Microsoft SDK supplies these files in the REDIST folder that is created when you install the SDK. You should copy these files from the REDIST folder into the WINDOWS\SYSTEM folder before you begin the examples in this book.

You will first learn to use the Voice Text object to add TTS services to Visual Basic applications. Once you know how to use the Voice Text object, you'll learn how to use the Voice Command and Voice Menu objects to add SR services to Visual Basic applications.

If you have not yet done so, start Visual Basic 4.0 and begin a new project. You'll use Visual Basic to complete all the examples in this section.

OLE Voice Text Object

The Voice Text object is used to provide access to the text-to-speech services of the installed TTS engine. Using the Voice Text object involves only a few simple steps. First, you must register your application with the SAPI services on the workstation. Then you can send text to the Voice Text object for playback. You can use several other methods to rewind, fast-forward, pause, and restart audio playback of the text message.

Using the Visual Basic Object Browser

When using Visual Basic, you can use the Tools | References menu option to load the object library into the Visual Basic design-time environment (see Figure 17.1).

Figure 17.1 : Loading the OLE Voice Text library into Visual Basic at design time.

The advantage of this method is that you can use the Visual Basic Object browser to view all the objects, methods, and properties of the library at design time (see Figure 17.2).

Figure 17.2 : Using the Visual Basic Object Browser to inspect the Voice Text library.

Note
Other VBA-compliant languages (Excel, Word, Access, and so on) cannot pre-load the object library. You can still use the SAPI OLE objects as described here, but you will not be able to use the object browser to view the object details at run-time.

Start a new Visual Basic project and add the following code to the general declarations section:

Dim objVText as Object ' declare TTS object variable

You'll use this variable throughout the project. This is the top-level object for all TTS services. Next you need to add code to the Form_Load event to initialize the TTS object. Also, you need to add code to the Form_Unload event to destroy the TTS object upon exit. Add the code shown in Listing 17.1 to your project.


Listing 17.1. Adding code to the Form_Load and Form_Unload events.
Private Sub Form_Load()
    '
    ' create voice text object
    '
    Set objVText = CreateObject("Speech.VoiceText")
    '
End Sub

Private Sub Form_Unload(Cancel As Integer)
    '
    ' destroy voice object
    '
    Set objVText = Nothing
    '
End Sub

Tip
It's always a good idea to destroy the object variable once you no longer need it. Setting the object equal to Nothing clears memory of any remaining references to the unused objects.

Using the Register Method to Connect to the TTS Engine

Once you create a valid TTS object, you must use the Register method to register your application with the TTS engine. There are two parameters that you can use with the Register method. The first parameter allows you to select the output device that the TTS engine will use for playback. You can leave this parameter empty in order to use the default device. You can also enter the name of any other valid line device such as an attached telephone handset. The second parameter is the name of the application that is requesting TTS services. This second parameter cannot be left blank.

To test the Register method, add a new command button to the project, and set its Name property to cmdReg and its caption to &Register. Then add the code shown in Listing 17.2 to the CmdReg_Click event.


Listing 17.2. Adding code to the cmdReg_Click event.
Private Sub cmdReg_Click()
    '
    ' register the app w/ TTS engine
    '
    Dim cDevice As String
    Dim cAppName As String
    '
    cDevice = "" ' use default value
    cAppName = "Voice Text Demo"
    '
    objVText.register cDevice, cAppName
    '
End Sub

Now save the form as VTEXT.FRM and the project as VTEXT.VBP, and run the project. When you press the Register button, you will register your application as ready for TTS services.

Using the Enable Property to Start and Stop the TTS Engine

Once you have registered your application with the TTS engine, you must set the Enabled property to TRUE before you can actually send text to the engine for output.

Add two new buttons to the form. Set the Name property and Caption property of one button to cmdEnable and &Enable, respectively. Set the Name and Caption properties of the next button to cmdDisable and &Disable. Then add the code shown in Listing 17.3.


Listing 17.3. Adding code to the cmdEnable and cmdDisable buttons.
Private Sub cmdDisable_Click()
    '
    ' disabling the engine for this site
    '
    objVText.Enabled = False
    '
End Sub

Private Sub cmdEnable_Click()
    '
    ' enable the engine for this site
    '
    objVText.Enabled = True
    '
End Sub

Note
The Enabled property affects all applications using TTS services at your workstation. Use this setting with caution. Changing the Enabled property to FALSE will disable all TTS services registered on your pc. For this reason, it is a good idea to check the Enabled property each time you attempt to speak text.

Using the Speak Method to Play Text

Once you have registered your application with the TTS engine and set the Enabled property to TRUE, you can send text to the engine for playback. The Speak method takes two parameters. The first parameter is the text to play back, and the second parameter is a flag parameter that can be used to establish the statement type and queue priority of the text being sent to the TTS engine.

Every text message sent to the TTS engine must be declared as one of seven types. The OLE Voice Text library has a set of pre-defined constants that can be used to set the statement type of the Speak method option flag. These types are shown in Table 17.1.

Table 17.1. Statement types used by the SAPI TTS engine.
Pre-Defined ConstantValue Description
Vtxtst_STATEMENT 1A neutral informative statement, such as You have new messages. This is the default type of speech.
Vtxtst_QUESTION 2A question, such as Do you want to save the file?
Vtxtst_COMMAND 4An instruction to the user, such as Insert the disk.
Vtxtst_WARNING 8A warning, such as Your printer is out of paper.
Vtxtst_READING 16Text that is being read from a document, such as an e-mail message.
Vtxtst_NUMBERS 32Text that is numeric and that should be read in numeric style.
Vtxtst_SPREADSHEET 64Text that is being read from a spreadsheet, such as columns of numbers.

Warning
The flag parameter of the Speak method is not optional, but how the value is used depends on the installed TTS engine. It is possible that setting this flag to various values will not result in different inflections of voice during playback.

The TTS engine is responsible for playing back messages sent in from all speech-enabled applications on your pc. Each message sent to the TTS engine is played back in the order in which it was received. You can use the flag parameter to control the priority level of the text message you send to the TTS engine. There are three priority levels for a message. The OLE library has a set of pre-defined constants to match the priority values. Table 17.2 shows each of the priority levels, their values, and a short description.

Table 17.2. Priority values used by the SAPI TTS engine.
Pre-Defined ConstantValue Description
Vtxtsp_VERYHIGH 128Play the text immediately, interrupting text that is currently being spoken, if any. The interrupted text resumes playing as soon as the very high-priority text is finished, although the interrupted text may not be correctly synchronized.
Vtxtsp_HIGH 256Play the text as soon as possible, after text that is currently being spoken but before any other text in the queue.
Vtxtsp_NORMAL 512Add the text to the end of the queue. This is the default priority.

To test the Speak method, you need to add a command button to the form along with a text box and several option buttons with two frame controls. Refer to Figure 17.3 and Table 17.3 as you arrange the controls on the form.

Figure 17.3 : Laying out the controls for adding the Speak method to the project.

Table 17.3. Control table for the Voice Text Demo form.
ControlProperty Setting
VB.Frame Name fraPriority
 Caption "Priority"
 Height 615
 Left 3120
 TabIndex 13
 Top 3120
 Width 3975
VB.OptionButton Name OptPriority
 Caption "&Very High"
 Height 255
 Index 2
 Left 2760
 TabIndex 16
 Top 240
 Width 1095
VB.OptionButton Name OptPriority
 Caption "&High"
 Height 255
 Index 1
 Left 1440
 TabIndex 15
 Top 240
 Width 1215
VB.OptionButton Name OptPriority
 Caption "&Normal"
 Height 255
 Index 0
 Left 240
 TabIndex 14
 Top 240
 Value -1 'True
 Width 1215
VB.Frame Name fraType
 Caption "Statement Types"
 Height 1095
 Left 3120
 TabIndex 5
 Top 1920
 Width 3975
VB.OptionButton Name optType
 Caption "Warning"
 Height 255
 Index 6
 Left 2760
 TabIndex 12
Top 240
 Width 975
VB.OptionButton Name optType
 Caption "Statement"
 Height 255
 Index 5
 Left 1440
 TabIndex 11
 Top 720
 Value -1 'True
 Width 1095
VB.OptionButton Name optType
 Caption "Spreadsheet"
 Height 255
 Index 4
 Left 1440
 TabIndex 10
 Top 480
 Width 1215
VB.OptionButton Name optType
 Caption "Reading"
 Height 255
 Index 3
 Left 1440
 TabIndex 9
 Top 240
 Width 975
VB.OptionButton Name optType
 Caption "Question"
 Height 255
 Index 2
 Left 240
 TabIndex 8
 Top 720
 Width 1095
VB.OptionButton Name optType
 Caption "&Numbers"
 Height 255
 Index 1
 Left 240
 TabIndex 7
 Top 480
 Width 1095
VB.OptionButton Name optType
 Caption "Command"
 Height 255
 Index 0
 Left 240
 TabIndex 6
 Top 240
 Width 1095
VB.TextBox Name txtSpeak
 Height 1575
 Left 3120
 MultiLine -1 'True
 ScrollBars 2 'Vertical
 TabIndex 4
 Top 240
 Width 3975
VB.CommandButton Name cmdSpeak
 Caption "&Speak"
 Height 495
 Left 1680
 TabIndex 3
 Top 240
 Width 1215

Note that the option buttons are built as members of control arrays. You'll use this array when you add the code behind the Speak command button. Now add the code from Listing 17.4 to the cmdSpeak_Click event.


Listing 17.4. Adding code to the cmdSpeak_Click event.
Private Sub cmdSpeak_Click()
    '
    ' get text and play it back
    '
    Dim iType As Integer ' statement type
    Dim iPriority As Integer ' queue priority
    Dim cText As String ' text to speak
    '
    ' read statement type selection
    iType = IIf(optType(0), vtxtst_COMMAND, iType)
    iType = IIf(optType(1), vtxtst_NUMBERS, iType)
    iType = IIf(optType(2), vtxtst_QUESTION, iType)
    iType = IIf(optType(3), vtxtst_READING, iType)
    iType = IIf(optType(4), vtxtst_SPREADSHEET, iType)
    iType = IIf(optType(5), vtxtst_STATEMENT, iType)
    iType = IIf(optType(6), vtxtst_WARNING, iType)
    '
    ' get priority value
    iPriority = IIf(OptPriority(0), vtxtsp_NORMAL, iPriority)
    iPriority = IIf(OptPriority(1), vtxtsp_HIGH, iPriority)
    iPriority = IIf(OptPriority(2), vtxtsp_VERYHIGH, iPriority)
    '
    ' get text to speak
    cText = txtSpeak.Text
    '
    objVText.Speak cText, iType + iPriority
    '
End Sub

This is the minimal set of commands needed to implement speech services. You can provide speech services by simply registering your application, enabling the engine, and executing the Speak method. However, there are additional properties and methods that you can use to control the behavior of the TTS engine.

Adjusting the Speed of Voice Playback

You can adjust the speed of audio playback using the Speed property. The speed of audio playback is measured in words per minute (wpm). The default speed of playback is 150wpm. Setting the Speed property to 0 causes the engine to speak at the lowest possible speed. Setting it to -1 results in the highest possible speaking speed.

You can use the Speed property to both read and write the playback value. Add a single command button to the form. Set its Name property to cmdSpeed and its Caption property to S&peed. Then add the code shown in Listing 17.5 to the cmdSpeed_click event.


Listing 17.5. Adding code to the cmdSpeed_Click event.
Private Sub cmdSpeed_Click()
    '
    ' determine min and max speed
    ' for this TTS engine
    '
    Dim iMin As Integer
    Dim iMax As Integer
    Static iDefault As Integer
    Dim cMsg As String
    '
    ' get default first time
    If iDefault = 0 Then
        iDefault = objVText.Speed ' get default speed
    End If
    '
    ' get slowest speed
    objVText.Speed = 0 ' set to slowest speed
    iMin = objVText.Speed ' get slowest speed
    '
    ' set fastest speed
    objVText.Speed = -1 ' set to fastest speed
    iMax = objVText.Speed ' get fastest speed
    '
    ' set back to default value
    objVText.Speed = iDefault
    '
    ' show results
    cMsg = "Slowest Speed: " & CStr(iMin) & Chr(13)
    cMsg = cMsg & "Fastest Speed: " & CStr(iMax) & Chr(13)
    cMsg = cMsg & "Default Speed: " & CStr(iDefault)
    '
    MsgBox cMsg, vbInformation, "Voice Command Speed Settings"
    '
End Sub

When you save and run the project, press the Speed button (be sure to press Register and Enabled first). You'll see a message box showing the speed limits of your TTS engine. Your message box should look like the one in Figure 17.4.

Figure 17.4 : Displaying the TTS engine speeds.

To test the effects of changing playback speed, add two buttons, one label control, and one text box control to the form. Use Table 17.4 and Figure 17.5 as guides when adding these controls.

Figure 17.5 : Adding the speed controls to the form.

Table 17.4. Controls for adjusting playback speed.
ControlProperty Setting
VB.CommandButton Name cmdSpGet
 Caption "&Get Speed"
 Height 300
 Left 4560
 TabIndex 20
 Top 3840
 Width 1215
VB.CommandButton Name cmdSpSet
 Caption "Se&t Speed"
 Height 300
 Left 5880
 TabIndex 19
 Top 3840
 Width 1215
VB.TextBox Name txtSpeed
 Height 300
 Left 3960
 TabIndex 17
 Top 3840
 Width 495
VB.Label Name lblSpeed
 Alignment 1 'Right Justify
 Caption "Speed:"
 Height 300
 Left 3120
 TabIndex 18
 Top 3840
 Width 615

Adding Playback Controls for TTS Services

You can also add the ability to pause, rewind, fast-forward, and restart TTS playback. The TTS fast-forward and rewind methods cause the engine to move the playback pointer approximately one sentence. Refer to Table 17.5 and Figure 17.5 to add the playback button controls to the form.

Table 17.5. Adding the playback buttons to the form.
ControlProperty Setting
VB.Frame Name fraAudio
 Caption "Playback"
 Height 2655
 Left 1560
 TabIndex 26
 Top 1440
 Width 1455
VB.CommandButton Name cmdAudio
 Caption "Stop"
 Height 375
 Index 4
 Left 1680
 TabIndex 27
 Top 3600
 Width 1215
VB.CommandButton Name cmdAudio
 Caption "Resume"
 Height 375
 Index 3
Left 1680
 TabIndex 25
 Top 3120
 Width 1215
VB.CommandButton Name cmdAudio
 Caption "Pause"
 Height 375
 Index 2
 Left 1680
 TabIndex 24
 Top 2640
Width 1215
VB.CommandButton Name cmdAudio
 Caption "Forward"
 Height 375
 Index 1
 Left 1680
 TabIndex 23
 Top 2160
 Width 1215
VB.CommandButton Name cmdAudio
 Caption "Rewind"
 Height 375
 Index 0
 Left 1680
 TabIndex 22
 Top 1680
Width 1215

After adding the command button array and the frame control to the form, add the code shown in Listing 17.6 behind the cmdAudio_Click event.


Listing 17.6. Adding code behind the cmdAudio_Click event.
Private Sub cmdAudio_Click(Index As Integer)
    '
    ' handle audio playback buttons
    '
    On Error GoTo cmdAudioErr
    '
    Select Case Index
        Case 0 ' rewind
            objVText.AudioRewind
        Case 1 ' forward
            objVText.AudioFastForward
        Case 2 ' pause
            objVText.AudioPause
        Case 3 ' resume
            objVText.AudioResume
        Case 4 ' stop
             objVText.StopSpeaking
    End Select
    '
    Exit Sub
    '
cmdAudioErr:
    MsgBox Error$, vbCritical, "Audio Playback Error [" & CStr(Err) & "]"
    '
End Sub

Getting TTS Status Reports with the IsSpeaking Property

You can use the IsSpeaking property to check the status of the TTS engine. The IsSpeaking property returns TRUE if the engine is currently speaking a message, FALSE when the engine is idle. You must check the property on a regular basis in order to get up-to-date status reports. The best way to do this is in a timed loop. In Visual Basic, this can be done using the timer control.

Add three new controls to the form: a new frame control (Name=fraIsSp, Caption=Is Speaking), a new label control (Name=lblIsSpeaking, Caption=False), and a timer control. Refer to Figure 17.6 for the position and size of the controls.

Figure 17.6 : Placing the IsSpeaking controls on the form.

After you place the controls on the form, add the code shown in Listing 17.7 to the Timer1_Timer event and the cmdEnabled_Click event.


Listing 17.7. Adding code to check the IsSpeaking property.
Private Sub Timer1_Timer()
    '
    ' update label display
    lblIsSpeaking = CStr(objVText.IsSpeaking)
    '
End Sub

Private Sub cmdEnable_Click()
    '
    ' enable the engine for this site
    '
    objVText.Enabled = True
    '
    ' add code to get voice speed
    cmdSpGet_Click
    '
    ' start timer to check IsSpeaking
    Timer1.Interval = 500
    Timer1.Enabled = True
    '
End Sub

After saving and running the program, you can register and enable the TTS services and enter text to be spoken. When you press the Speak button, you can watch the Is Speaking label change from False to True and back again to False.

Establishing a TTS Callback in Visual Basic 4.0

If you are using Visual Basic 4.0 as the platform for the OLE Voice Text library, you can use the Callback property to establish an asynchronous callback from the TTS engine to your Visual Basic program. This link is established by adding a class module to your Visual Basic program. This class module must contain two methods: SpeakingStarted and SpeakingDone. These methods will be automatically invoked by the TTS engine at the appropriate time.

To establish a TTS callback in Visual Basic 4.0, first add a class module. Set its Name property to clsCallBack, its Instance property to MultiUse, Creatable, and its Public property to TRUE. Then add the code in Listing 17.8.


Listing 17.8. Adding methods to the clsCallBack class module.
Public Function SpeakingDone()
    '
    ' this method will execute when the
    ' TTS engine stops speaking text
    '
    frmVText.lblCallBack = "TTS Engine Idle"
    '
End Function

Public Function SpeakingStarted()
    '
    ' this method will execute when the
    ' TTS engine starts speaking text
    '
    frmVText.lblCallBack = "TTS Engine Active"
    '
End Function

The code in Listing 17.8 creates the class and methods required to link the TTS engine with your Visual Basic application. The next step is to initialize the Callback property. In this step, you are telling the TTS engine the name of the project and the name of the class module that has the two required methods.

To do this, add another command button, frame control, and label control to the form. Use Figure 17.7 and Table 17.6 to place and size the controls.

Figure 17.7 : Placement of the Callback controls on the form.

Table 17.6. The Callback controls.
ControlProperty Setting
VB.CommandButton Name cmdCallBack
 Caption "&CallBack"
 Height 495
 Left 240
 TabIndex 32
 Top 2760
 Width 1215
VB.Frame Name fraCallBack
 Caption "Call Back"
 Height 735
 Left 240
 TabIndex 30
 Top 3360
 Width 1215
VB.Label Name lblCallBack
 Alignment 2 'Center
 Height 375
 Left 120
 TabIndex 31
 Top 240
Width 975

After adding the controls, place the code shown in Listing 17.9 behind the CallBack_Click event.


Listing 17.9. Adding the code to the CallBack_Click event.
Private Sub cmdCallBack_Click()
    '
    ' register call back for VB4
    '
    objVText.Callback = "VText.clsCallBack"
    '
End Sub

This code sets the Callback property and informs the TTS engine of the application and class module to use for sending notification messages.

Warning
Be sure to include the proper application name (found by inspecting the ProjectName value on the Tools | Project command dialog box) and the proper class name (check the Name property of the class module). Failure to do this will result in OLE Automation error 440.

After adding the callback controls, you can save the form (VTEXT.FRM) and the project (VTEXT.VBP) and run the application. After registering and enabling the application, establish the callback by pressing the CallBack button. Then, when you have the TTS engine speak text in the text box, you'll see the TTS engine status messages appear in the Callback frame.

You now know how to use all the methods and properties of the Voice Text object. Next, you'll learn how to use the Voice Command objects to make Visual Basic applications listen to a human voice and translate the audio input into computer commands from a menu.

OLE Voice Command Objects

The Microsoft SR engine is implemented in an OLE object library called VCAUTO.TLB. This file can be added to the Visual Basic 4.0 design-time library through the Tools | References menu, and then you can view the library's objects, methods, and properties using the View | Object Browser option on the main menu.

The OLE Voice Command library contains two objects:

Note
In order to use these examples, you need to have the Microsoft Speech SDK installed on your machine and have the files from the REDIST folder (created when you install the Speech SDK) copied to your WINDOWS\SYSTEM folder. You also need to have an SR engine installed and running. All the examples here were created using the Microsoft Voice SR/TTS engine that ships with Microsoft Phone.

Creating the Voice Command Menu Object

The first step in the process is the creation and initialization of a programming object variable. This variable will act as the top-level object for accessing the SR engine.

Load Visual Basic and start a new project. You need to create two form-level variables that will be used throughout the program. You also need to add code to the Form_Load and Form_Unload events. Listing 17.10 shows the code for these events and the declaration section.


Listing 17.10. Adding code to the Form_Load and Form_Unload events.
Option Explicit

Dim objVCmd As Object
Dim objVMenu As Object

………

Private Sub Form_Load()
    '
    ' create new voice command object
    '
    Set objVCmd = CreateObject("Speech.VoiceCommand")
    '
End Sub

Private Sub Form_Unload(Cancel As Integer)
    '
    ' destroy the voice command objects
    '
    Set objVCmd = Nothing
    Set objVMenu = Nothing
    '
End Sub

Using the Register Method to Connect to the SR Engine

After creating the Voice Command object, you can use the Register method to connect the object to the installed SR engine. Add a command button to the form. Set its Name property to cmdReg and its Caption property to &Register. Then add the code shown in Listing 17.11.


Listing 17.11. Adding code to the cmdReg_Click event.
Private Sub cmdReg_Click()
    '
    ' register w/ SR engine
    '
    objVCmd.Register "" ' use default location
    '
End Sub

The Register method of the Voice Command object takes only one parameter: the device identifier. Passing an empty string results in registering the default input device (usually an attached microphone).

Using the Awake Property to Start and Stop SR Processing

Before the SR engine will attempt to interpret audio input, you must "wake it up" by setting the Awake property to TRUE. You can tell the SR engine to stop listening for commands by setting the Awake property to FALSE.

Add two buttons to the form, named CmdAwake and CmdSleep. Set their Caption properties to &Awake and &Sleep, respectively. Finally, add a label control named lblAwake. This label will contain a status message telling you when the SR engine is listening to you. Then add the code shown in Listing 17.12.


Listing 17.12. Adding code to the cmdAwake and cmdSleep buttons.
Private Sub cmdAwake_Click()
    '
    ' wake up the SR engine
    '
    objVCmd.Awake = True
    lblAwake.Caption = "SR Engine is Awake"
    '
End Sub


Private Sub cmdSleep_Click()
    '
    ' tell SR engine to stop listening
    '
    objVCmd.Awake = False
    lblAwake.Caption = "SR Engine is Sleeping"
    '
End Sub

Each time you press the Awake button or the Sleep button, the contents of the lblAwake control are updated.

Creating the Menu Object

After establishing the TTS connection and enabling it, you must create a menu object and fill it with menu commands. Then you can activate the menu, and it will respond to your voice.

First, add two new buttons to the form. Set their Name properties to cmdCreate and cmdAdd. Set their Caption properties to &Create Menu and &Add Menu. Next, add the code shown in Listing 17.13 behind the cmdCreate_Click event.


Listing 17.13. Adding code to the cmdCreate and cdmAdd buttons.
Private Sub cmdCrMenu_Click()
    '
    ' create a new menu
    '
    Dim iType As Integer
    Dim cMenuCmd As String
    Dim cMenuState As String
    Dim lMenuLangID As Long
    Dim cDialect As String
    Dim cAppName As String
    '
    ' set default stuff
    cAppName = App.EXEName ' this app
    cMenuState = frmVCmd.Caption ' this window
    cDialect = "" ' use default
    lMenuLangID = 1033 ' US lang nbr
    '
    ' get menu creation type
    iType = IIf(optMenuCmd(0), vcmdmc_CREATE_NEW, iType)
    iType = IIf(optMenuCmd(1), vcmdmc_CREATE_ALWAYS, iType)
    iType = IIf(optMenuCmd(2), vcmdmc_CREATE_TEMP, iType)
    iType = IIf(optMenuCmd(3), vcmdmc_OPEN_ALWAYS, iType)
    iType = IIf(optMenuCmd(4), vcmdmc_OPEN_EXISTING, iType)
    '
    ' now create an empty menu
    Set objVMenu = objVCmd.MenuCreate(cAppName, cMenuState, lMenuLangID, cDialect, ÂiType)
    '
End Sub

Creating a menu involves setting several parameters. First, you need to tell the SR engine the name of the application that is requesting services. This must be a unique name, since all responses will be directed by the SR engine, based on this published application name. Next, you need to initialize the MenuState parameter. The menu state is much like the level of a menu. It groups menu items together. All the menus in the same group are considered at the same time when the SR engine is analyzing audio input.

The lMenuLangID helps the engine interpret user input. Most SR engines support only one language. Setting this parameter tells the SR engine what language you plan to speak. You can set the values using the pre-defined Visual Basic 4.0 constants, or by looking up the values in the Visual Basic help files under "Collating Order." Table 17.7 shows the possible language constants in Visual Basic.

Table 17.7. Visual Basic language constants.
Language IDDescription
dbLangGeneral English, German, French, Portuguese, Italian, and Modern Spanish
dbLangArabic Arabic
dbLangCzech Czech
dbLangCyrillic Russian
dbLangDutch Dutch
dbLangGreek Greek
dbLangHebrew Hebrew
dbLangHungarian Hungarian
dbLangIcelandic Icelandic
dbLangNordic Nordic languages (Microsoft Jet database engine version 1.0 only)
dbLangNorwdan Norwegian and Danish
dbLangPolish Polish
dbLangSwedfin Swedish and Finnish
dbLangSpanish Traditional Spanish
dbLangTurkish Turkish

Note
If you are using Visual Basic 4.0, you can find these constants using the Object Browser. Once you find them, make sure you use only the language portion of the constant. SAPI needs only the language number, not the country code and other items stored in the collating-order constants.

The next value is the Dialect parameter. This value can control the character of the spoken word. It is also possible that the SR engine does not use this parameter. A NULL string will use the default dialect built into the speech engine.

Lastly, you need to indicate the type of menu you wish to work with. The SAPI model defines three ways to create new menus and ways to open existing menus. Table 17.8 shows the various versions of the MenuType parameter.

Table 17.8. The MenuType parameters.
Pre-Defined VB4 Constant
Value
Description
Vcmdmc_CREATE_NEW
2
Creates an empty menu with the given name. If a menu by that name already exists in the voice command database, the method fails. The new menu is stored in the database when the Voice Menu object is released.
Vcmdmc_CREATE_ALWAYS
4
Creates an empty menu with the given name. If a menu by that name already exists in the voice command database, it is erased. The new menu is stored in the database when the Voice Menu object is released.
Vcmdmc_CREATE_TEMP
1
Creates an empty menu with the given name. If a menu by that name already exists in the voice command database, the method fails. The new menu is temporary and is discarded when the Voice Menu object is released.
Vcmdmc_OPEN_ALWAYS
8
Opens an existing menu with the given name. If the menu does not exist, the method creates a new, empty menu. The new menu is stored in the database when the Voice Menu object is released.
Vcmdmc_OPEN_EXISTING
16
Opens an existing menu. If the menu does not exist, the method fails.

Creating the empty menu is only half of the process. Creating the menu object does not populate that menu with commands. You must add voice command items to the new menu using the Add method of the Voice Menu object.

Adding Commands to the Voice Menu Object

Once you have created the Voice Menu object, you can start adding actual commands to the menu. This is done with the Add method of the Voice Menu object. The Add method takes the four parameters shown in Table 17.9.

Table 17.9. The Add method parameters.
ParameterDescription
identifier Unique number for the command. The CommandSpoken property is set to this number when the command is recognized.
command Voice command string-for example, Open the file. This is the command that users will speak.
category String that indicates the category to which the command belongs.
description String that describes the action performed by the command. This is for comment purposes only.

The identifier parameter must be a unique number. This value is returned when the SR engine recognizes the spoken command in the command parameter. The category parameter is used to organize lists of commands. This is similar to using "File," "Edit," or "Help"-type menu categories. The description parameter is used only for comment purposes and does not affect the performance of the SR engine at all.

To add some commands to the menu you created earlier, add a new button to the project. Set its Name property to cmdAdd and its Caption property to Add Items. Then add the code shown in Listing 17.14.


Listing 17.14. Adding code to the cmdAdd_Click event.
Private Sub cmdAdd_Click()
    '
    ' add a new item to the menu
    '
    Dim x As Integer
    '
    For x = 1 To 4
        lMenuIndex = x
        objVMenu.Add lMenuIndex, "Line " & CStr(x), "Item List", "Make an Item
                  ÂList"
        '
        objVMenu.hWndMenu = Me.hWnd ' for this window
        objVMenu.Active = True ' turn it on
        '
    Next x
    '
    MsgBox "Menu Complete"
    '
End Sub

Your form should look something like the one in Figure 17.8.

Figure 17.8 : The VCMD screen layout.

There are two other steps to completing the process of adding commands to a menu. First, all commands added to a menu are global unless they are assigned a window handle. Global menus can be called at any time during a Windows session, regardless of what window is active or what programs are loaded. If a menu command is assigned to a particular window, it will only be available when the window is loaded and has focus.

Tip
It is a good idea to limit the use of global menu commands. The more global commands you have registered, the more analysis will be required before the SR engine can identify your command. Additional commands also increase the error rate of the SR engine.

After entering the code from Listing 17.14, save and run the project. Press Register and Awake; then press Create Menu and Add Items. This will add the four items to the list of available voice commands. Figure 17.9 shows a list of the available voice commands. You can see the new commands added by the VCMD application at the end of the command list.

Figure 17.9 : Viewing the active voice commands.

Note
Figure 17.9 shows the output of the "What Can I Say?" voice command for Microsoft Voice. If you are using another SR/TTS engine, you may have a different method for viewing the active voice commands.

At this point, you have created a new voice command menu and added new commands that can be recognized by the engine. However, you still need to add additional code to get the application to respond to the menu commands themselves.

Using the CommandSpoken Property to Respond to Menu Commands

Once you have registered commands with the SAPI system, you need to have a way to detect when commands have been spoken and to react to them accordingly. To do this, you need to check the CommandSpoken property of the Voice Command object.

The CommandSpoken property will contain the identifier number of the menu command the SR engine has recognized. You can then use this number to execute program code.

You need to regularly check the CommandSpoken property using a timer loop. To do this, you need to add one timer control and three label controls to the form. After placing the timer control on the form, add the three label controls. Name the first one lblResults and set its BorderStyle property to Fixed Single and its Alignment property to Centered. Set the name of the second label control to lblStatus and give it the same properties as the lblResults control. Finally, set the name of the third label control to lblPolling with the Caption set to Polling Results. Refer to Figure 17.10 for sizing and placement of these three controls.

Figure 17.10 : Adding the controls for polling the CommandSpoken property.

Now add two new lines of code to the Form_Load event. This code will start the timer cycle (see Listing 17.15).


Listing 17.15. Adding timer code to the Form_Load event.
Private Sub Form_Load()
    '
    ' create new voice command object
    '
    Set objVCmd = CreateObject("Speech.VoiceCommand")
    '
    Timer1.Interval = 500
    Timer1.Enabled = True
    '
End Sub

Finally, you need to add code to the Timer1_Timer event. This code will fire each time the timer interval loop reaches zero. Here you can test the value of CommandSpoken and respond according to the number returned. Add the code shown in Listing 17.16.


Listing 17.16. Adding code to respond to the CommandSpoken property.
Private Sub Timer1_Timer()
    '
    ' look for menu hit
    '
    Dim lHeard As Long
    '
    lHeard = objVCmd.CommandSpoken
    lblstatus = "Menu ID: [" & CStr(lHeard) & "]"
    '
    Select Case lHeard
        Case 0 ' no command
            ' na
        Case 1 ' beep command
            lblResults = "Line 1"
        Case 2
            lblResults = "Line 2"
        Case 3
            lblResults = "Line 3"
        Case 4
            lblResults = "Line 4"
    End Select
    '
    lHeard = 0
    objVCmd.CommandSpoken = 0
    '
End Sub

As soon as you get a non-zero value, you can check this value against a list of known identifier numbers and then fire off your own code based on the returned value. In this case, you will update the lblStatus box to show the command value returned, and the lblResults box will be updated to show the action to be taken.

You can test this by saving and running the project. After pressing Register, Awake, Create Menu, and Add Items, you will be able to speak any of the four registered commands (Line 1 through Line 4), and you'll see the lblResults label reflect the spoken command. In a real program, this code could fire off new forms, load files, save data to disk, and so on.

Establishing an SR Callback in Visual Basic 4.0

In Visual Basic 4.0, you establish a callback notification using a class module. This is a better method for receiving information about the spoken commands than polling the CommandSpoken property. To create a callback link between SAPI and your Visual Basic program, you must first add a class module to the project and then add two subroutines:

To add these elements, add a new class module to your project (Insert | Class Module) and set its Name property to clsCallBack. Also set its Instancing property to Creatable, Multiuse and its Public property to TRUE. Then add the code shown in Listing 17.17.


Listing 17.17. Adding the Callback routines to the clsCallBack module.
Function CommandRecognize(szCommand As String, dwID As Long)
    '
    ' trap recognized command
    '
    frmVCmd.lblcallback = szCommand & " [" & CStr(dwID) & "]"
    '
End Function

Function CommandOther(szCommand As String, szApp As String, szState As String)

End Function

Notice that the CommandRecognize function returns two parameters. The first one is the actual command string spoken by the user. The second is the identifier number of the command. The single line of code added to the routine will update a label control with the results of the spoken command.

The CommandOther function allows you to trap commands that were meant for other applications. In this way, you can monitor all voice commands from a single location. For now, you do not need to add any code to this routine.

Next, you need to add two more label controls and one button control to the project. Add a label control and set its Name property to lblCallBack, its Alignment property to Centered, and its BorderStyle property to Fixed Single. Set the name of the second label control to lblCBResults and its Caption to CallBack Results. Now add a command button to the project with its Name property set to cmdCallBack and its Caption set to CallBack.

Finally, you need to add code behind the cmdCallBack_Click event to actually register the callback notification. Listing 17.18 shows you how to do this.


Listing 17.18. Adding code to the cmdCallBack_Click event.
Private Sub cmdCBack_Click()
    '
    ' register callback
    '
    objVCmd.Callback = "Vcmd.clsCallBack"
    '
End Sub

Now save and run the project. You'll see the lblCallBack label show the exact command spoken along with the identifier number. This will match the information shown in the lblResults box.

Creating List Commands for the Voice Menu Object

The Voice Menu object allows you to create a list of items to be applied to a single command. For example, you could create a command that displays a customer address (such as Show Smith and Sons). But what if you wanted to show every customer address? Instead of creating a menu command for each address, you can create a list of customers and then apply that list to a special command string that has the list name as a part of the command (for example, Show <CustomerName>).

You can create lists using the ListSet method of the Voice Menu object. The ListSet method accepts two parameters. The first is the name of the list. The second parameter is the list of items, each separated by Chr(0).

To test this, add a new command button to the form. Set its Name property to cmdList and its Caption to Make List. Then add the code shown in Listing 17.19.


Listing 17.19. Adding code behind the cmdList_Click event.
Private Sub cmdList_Click()
    '
    ' add a list command
    ' build a list
    ' activate the menu
    '
    lMenuIndex = lMenuIndex + 1
    objVMenu.Active = False
    objVMenu.Add lMenuIndex, "Show <CustomerName>", "Name List", "Display Names"
    objVMenu.ListSet "CustomerName", 3, "Smith" & Chr(0) & "Lee" & Chr(0) & Â"Shannon" & Chr(0)
    objVMenu.Active = True
    '
End Sub

Notice that you must first create a valid menu command that refers to the list. Also, it is a good idea to set the Active property to FALSE while you edit a menu and set it back to TRUE when you are done.

Since you have added a new command to the menu, you also need to update the Timer1_Timer event to look for the new command. Modify the code in Timer1_Timer to match the code in Listing 17.20.


Listing 17.20. Modifying the Timer1_Timer event.
Private Sub Timer1_Timer()
    '
    ' look for menu hit
    '
    Dim lHeard As Long
    '
    lHeard = objVCmd.CommandSpoken
    lblstatus = "Menu ID: [" & CStr(lHeard) & "]"
    '
    Select Case lHeard
        Case 0 ' no command
            ' na
        Case 1 ' beep command
            lblResults = "Line 1"
        Case 2
            lblResults = "Line 2"
        Case 3
            lblResults = "Line 3"
        Case 4
            lblResults = "Line 4"
        Case 5
            lblResults = "Show"
    End Select
    '
    lHeard = 0
    objVCmd.CommandSpoken = 0
    '
End Sub

Now save and run the project. After starting the SR engine (press Register, Awake, Create Menu, Add Items, and CallBack), press the Make List button to add the new commands to the list. At this point, you should be able to view the command list from Microsoft Voice by invoking the "What Can I Say?" command. Your screen should look something like the one in Figure 17.11.

Figure 17.11 : Viewing the new command list.

Now when you speak the command Show Lee, you'll see the command appear in the lblCallBack window (see Figure 17.12).

Figure 17.12 : The results of speaking a list command.

Notice that the callback window shows the complete spoken command (that is, both the command and the list item), but the CommandSpoken window just displays the word Show. The CommandSpoken property can return only the identifier of the command recognized by the SR engine, not the actual command itself. This means that if you plan to use lists as part of your voice commands, you will have to use the Callback property and class module to return the complete spoken command.

Tip
Since you must use the Callback property to retrieve the complete results of spoken list commands, you will not be able to use lists in other VBA-compatible languages that do not allow the creation of class modules. As of this writing, only Visual Basic 4.0 allows the creation of class modules.

Removing Commands from the Voice Menu Object

You can remove commands from the Voice Menu object using the Remove method. It is a good idea to remove any commands that are no longer needed. The fewer commands the SR engine has to deal with, the faster and more accurate the engine will be.

Add one more button to the form. Set its Name property to cmdRemove and its Caption property to Remove. Now add the code shown in Listing 17.21 to the cmdRemove_Click event.


Listing 17.21. Adding code to the cmdRemove_Click event.
Private Sub Command1_Click()
    '
    ' remove menus from the list
    '
    Dim lLoop As Long
    '
    For lLoop = lMenuIndex To 1 Step -1
        objVMenu.Remove (lLoop)
    Next lLoop
    '
    lMenuIndex = 0
    '
    MsgBox "Menus Removed"
    '
End Sub

Tip
In this code example, you are creating a temporary menu. This menu will be removed automatically when the program exits. It is still a good practice to remove old menu items, because these items take up storage space and can cause slower recognition.

Summary

In this chapter, you learned how to use the SAPI OLE objects to create real, working TTS and SR Windows programs using Visual Basic 4.0. You learned that there are two OLE libraries for use with Visual Basic 4.0 and all other VBA-compatible languages:

The Voice Text library provides TTS services and the Voice Command library provides SR services.

You learned how to use the Voice Text object to register and enable the TTS engine and how to send text to the engine for playback. You also learned how to adjust the speed of playback and how to add rewind, pause, fast-forward, and resume options to TTS playback. Finally, you learned how to use the IsSpeaking property in a timer loop and the Callback property to get notification on the status of the TTS engine.

You learned how to use the Voice Command object and the Voice Menu object to register and awaken the SR engine. You also learned how to create and populate command menus and how to use the CommandSpoken and Callback properties to return the identifier of the command spoken and to respond by executing code associated with the command. You also learned how to create command lists to allow users to speak a single command with replaceable parameters.

In the next chapter, you'll learn more about command lists and parameters used by the grammar engine that underlies the Speech system. You'll also learn how to use control tags to improve TTS engine playback and about the International Phonetic Alphabet and how it is used to improve both SR and TTS services.