by robbiex » Thu Nov 26, 2009 10:57 am
There are multiple problems with setting a default TTS Voice in
Windows Server 2008 R2 and Windows 7 64 bit edition.
The Control Panel TTS Voice option lists only properly registered
64 bit Voices. Microsoft's Anna is the only 64 bit Voice that is
supplied with the operating systems. Loquendo's Kate, which is
probably the highest quality TTS Voice that is currently available,
has both 32 and 64 bit versions. Kate is difficult to obtain in small
quantities and is very expensive. The next best alternative is
believed to be the Acapela-Group Voice Heather. It is only
currently available in a 32 bit version for the Windows operating
systems. (64 bit Heather is available for Apple's Snow Leopard).
A third possibility is Cepstral's Callie. I believe that this is still
available only as a 32 bit Voice. The demo version has some glitches.
Testing with Microsoft's sample program, TTSApp, revealed two
problems that we haven't encountered with any other Voice. Text
highlighting doesn't track properly even when the TTS has not been
interrupted with a "nag" statement and the program hangs when a
stop is requested before the end of the text that is being spoken.
As others have already mentioned in this forum, there is a well known
fix that permits the display of all the 32 and 64 bit installed Voices
and the selection of any as the system default Voice. This is to
execute:
C:\Windows\SysWOW64\Speech\SpeechUX\sapi.cpl
rather than using the Control Panel TTS Voice selection.
This fix works properly except for one unfortunate problem
that occurs with use of a Visual Basic program to interface with the
SAPI 5+ library.
The Visual C/C++ statement
hr = m_cpVoice.CoCreateInstance( CLSID_SpVoice );
correctly sets up a Voice object regardless if the default
system Voice is 32 or 64 bit.
The Visual Basic statement
Voice = New SpeechLib.SpVoice
results in a system hang or program abort if the default Voice has
been set to anything other than a properly registered 64 bit Voice
like Anna. "Try Catch" doesn't catch this error.
Note that Set Voice = CreateObject("SAPI.SpVoice") also fails with the
same error.
There is no easy current solution to this problem. It may, however, be
less of concern as more 64 bit TTS Voices become available.
First, Microsoft should certainly change the Control Box TTS selection
to use the same code as "C:\Windows\SysWOW64\Speech\SpeechUX\sapi.cpl".
Second, Microsoft should correct the problem with the way in which
The Visual Basic statement "Voice = New SpeechLib.SpVoice" functions.
There is no reason for this not to have the same capability as its
Visual C/C++ counterpart.
We have had considerable discussions with Microsoft about this
problem and they appear to be unwilling to correct it.
There are several possible fixes as follows.
Application programs that use TTS could be written completely
in Visual C/C++, the creation of the Voice object could be in an
Visual C ActiveX control or a wrapper could be used to access
CoCreateInstance.
It may be possible to modify the Voice registry entries so
that 32 bit Voices work with the 64 bit Control Panel TTS listing.
It is easy to modify the registry so that the Voices are displayed.
There is, however, a consistent error message regarding a failure of
audio output. The TTS registry entries are generally well documented.
Unfortunately, there are multiple entries for which there is no publicly
available documentation. This plus a lack of technical assistance from
either the third party vendors of TTS Voices or Microsoft makes it
impossible to resolve the registry entry problem at this time.
These registry issues are not a concern when you use
"C:\Windows\SysWOW64\Speech\SpeechUX\sapi.cpl".
Finally, the registry entries can be programmatically changed so
that the default Voice meets the requirements of
"Voice = New SpeechLib.SpVoice" only for this one statement. The
original default entries are then immediately replaced prior to further
usage of the Voice object.
It is far from perfect, but we have chosen this approach as the
best current compromise for our programming environment. One
unfortunate limitation is that Windows 7 64 bit and Windows Server
2008 R2 use different TTS registry directory paths and Values for
DefaultTokenId. Note that these operating systems are identified by
the same "6.1" major and minor version numbers and it is necessary
to check both the version numbers and the operating system names.
Incidentally, an invaluable tool for working with the registry is
Registry Workshop for X64. It can do copy/paste in addition to
multiple other helpful registry operations.
The following code snippet demonstrates the methodology.
'Created inside Form_Load and released inside Form_Unload.
Dim WithEvents Voice As SpeechLib.SpVoice
Dim modifyRegistryFlag As Boolean = False
'Code snippet from Form_Load
Dim ret As Integer
Dim keyName, userRoot, subkeys, defaultString, oldString As String
Dim OSName, OSPlatform, OSVersion, versionNumbers As String
keyName = ""
userRoot = ""
subkeys = ""
defaultString = ""
oldString = ""
VersionNumbers = ""
OSName = Trim(My.Computer.Info.OSFullName.ToString)
OSPlatform = Trim(My.Computer.Info.OSPlatform.ToString)
OSVersion = Trim(My.Computer.Info.OSVersion.ToString)
'MessageBox.Show(OSName & vbCrLf & OSPlatform & vbCrLf & OSVersion)
If Len(OSVersion) >= 3 Then
versionNumbers = Mid(OSVersion, 1, 3)
'MessageBox.Show(versionNumbers)
Select Case versionNumbers
Case versionNumbers = "6.0"
ret = InStr(1, OSName, "Microsoft" & Chr(174) & " Windows Server" & Chr(174) & " 2008")
If ret = 1 Then
modifyRegistryFlag = True
'Registry directory for Windows Server
userRoot = "HKEY_CURRENT_USER"
subkeys = "Software\Microsoft\Speech\Voices"
'Default string for Windows Server
defaultString = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\MS-Anna-1033-20-DSK"
Else
MessageBox.Show("Cannot Identity Operating System Name starting at: " & ret & vbCrLf & OSVersion & vbCrLf & OSName & vbCrLf & "It should be Microsoft Windows Server 2008", "TTS Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1)
Me.Close()
End If
Case versionNumbers = "6.1"
ret = InStr(1, OSName, "Microsoft Windows 7")
If ret = 1 Then
modifyRegistryFlag = True
'Registry directory for Windows 7 64 bit
userRoot = "HKEY_CURRENT_USER"
subkeys = "Software\Microsoft\Speech\Voices"
'Default string for Windows 7 64 bit
defaultString = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\MS-Anna-1033-20-DSK"
ElseIf InStr(1, OSName, "Microsoft Windows Server 2008 R2") = 1 Then
modifyRegistryFlag = True
'Registry directory for Server 2008 R2
userRoot = "HKEY_USERS"
subkeys = ".DEFAULT\Software\Microsoft\Speech\Voices"
'Default string for Server 2008 R2
defaultString = "HKEY_CURRENT_USER\SOFTWARE\Microsoft\Speech\Voices\Tokens\MS-Anna-1033-20-DSK"
Else
MessageBox.Show("Cannot Identity Operating System Name starting at: " & ret & vbCrLf & OSVersion & vbCrLf & OSName & vbCrLf & "It should be Microsoft Windows 7 or Windows Server 2008 R2", "TTS Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1)
Me.Close()
End If
Case Else
End Select
End If
keyName = userRoot & "\" & subkeys
If modifyRegistryFlag = True Then
'Retrieve name of default Voice
oldString = ""
Try
oldString = Registry.GetValue(keyName, "DefaultTokenId", "-1")
Catch ex As Exception
MessageBox.Show(ex.Message.ToString, "TTS Error:Get Current Default Voice", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1)
Exit Sub
End Try
If oldString = "-1" Then
MessageBox.Show("No DefaultTokenId", "TTS Error:Get Current Default Voice", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1)
Exit Sub
End If
'Set default name to Anna if she is not the default
If oldString <> defaultString Then
'MessageBox.Show(oldString & vbCrLf & defaultString)
Try
Registry.SetValue(keyName, "DefaultTokenId", defaultString, RegistryValueKind.String)
Catch ex As Exception
MessageBox.Show(ex.Message, "TTS Error:Set Default Voice", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1)
Exit Sub
End Try
End If
End If
'Create the voice object
Try
Voice = New SpeechLib.SpVoice
Catch err As Exception
MessageBox.Show("Error Creating SpeechLib Voice" & vbNewLine & err.Message, "TTS Error", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1)
Exit Sub
End Try
'Restore the original default Voice before further use of the Voice object
If modifyRegistryFlag = True Then
If oldString <> defaultString Then
Try
Registry.SetValue(keyName, "DefaultTokenId", oldString, RegistryValueKind.String)
Catch ex As Exception
MessageBox.Show(ex.Message, "TTS Error:Restore Default Voice", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1)
Exit Sub
End Try
End If
End If
Lastly, I should mention that one important reason for wanting to
change the system default TTS Voice is to provide an alternate Voice
choice for programs like MapPoint which automatically select the system
default Voice.
There is no other user option for changing MapPoint's Voice.
There is a MapPoint registry key at
HKEY_CURRENT_USER\Software\Microsoft\MapPoint\17.0\USA\PreferredTTSEngine
that looks as if the TTS Voice can be selected. Unfortunately, there
is no documentation for this entry and changing the Voice name doesn't
override the system default Voice.