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.
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.
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. |
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.
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. |
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.
Pre-Defined Constant | Value | Description |
Vtxtst_STATEMENT | 1 | A neutral informative statement, such as You have new messages. This is the default type of speech. |
Vtxtst_QUESTION | 2 | A question, such as Do you want to save the file? |
Vtxtst_COMMAND | 4 | An instruction to the user, such as Insert the disk. |
Vtxtst_WARNING | 8 | A warning, such as Your printer is out of paper. |
Vtxtst_READING | 16 | Text that is being read from a document, such as an e-mail message. |
Vtxtst_NUMBERS | 32 | Text that is numeric and that should be read in numeric style. |
Vtxtst_SPREADSHEET | 64 | Text 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.
Pre-Defined Constant | Value | Description |
Vtxtsp_VERYHIGH | 128 | Play 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 | 256 | Play the text as soon as possible, after text that is currently being spoken but before any other text in the queue. |
Vtxtsp_NORMAL | 512 | Add 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.
Control | Property | 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.
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.
Control | Property | 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 |
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.
Control | Property | 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
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.
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.
Control | Property | 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.
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. |
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
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).
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.
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.
Language ID | Description |
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.
Pre-Defined VB4 Constant | Description | |
Vcmdmc_CREATE_NEW | 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 | 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 | 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 | 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 | 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.
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.
Parameter | Description |
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.
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.
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.
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. |
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. |
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.