Windows 7 64 Bit TTS Voice Selection: Problems & Possible Solutions

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).

There is a fix that permits the display of all 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.

The Visual C/C++ statement

    hr = m_cpVoice.CoCreateInstance( CLSID_SpVoice );

correctly sets up a Voice object regardless of the default
system Voice.

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 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.

Third, 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.

Fourth, it may be possible to modify the Voice registry entries so
that 32 bit Voices work with the 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".

Fifth, One important reason for being able 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.

Sixth, the registry entries could be programmatically changed so
that the default Voice meets the requirements of
"Voice = New SpeechLib.SpVoice" only for this one statement. The
original entries are then 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.
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 keyName, userRoot, subkeys, defaultString, oldString As String
        Dim OSName, OSPlatform, OSVersion As String
        OSName = Trim(My.Computer.Info.OSFullName.ToString)
        OSPlatform = Trim(My.Computer.Info.OSPlatform.ToString)
        OSVersion = Trim(My.Computer.Info.OSVersion.ToString)
        If InStr(1, OSVersion, "6.1") >= 1 Then
            modifyRegistryFlag = True
            If InStr(1, OSName, "Microsoft Windows 7") >= 1 Then
                'Registry directory path for Windows 7 64 bit
                userRoot = "HKEY_CURRENT_USER"
                subkeys = "Software\Microsoft\Speech\Voices"
                'Default DefaultTokenId string for Windows 7 64 bit
                defaultString = "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Speech\Voices\Tokens\MS-Anna-1033-20-DSK"
            Else
                If InStr(1, OSName, "Microsoft Windows Server 2008 R2") >= 1 Then
                    'Registry directory path for Server 2008 R2
                    userRoot = "HKEY_USERS"
                    subkeys = ".DEFAULT\Software\Microsoft\Speech\Voices"
                    'Default DefaultTokenId string for Server 2008 R2
                    defaultString = "HKEY_CURRENT_USER\SOFTWARE\Microsoft\Speech\Voices\Tokens\MS-Anna-1033-20-DSK"
                End If
            End If
        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

        'Creates 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

        If modifyRegistryFlag = True Then
            'Restore original default Voice
            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

Robbie
Answer
Answer
I have used System.Speech with C# and .NET version 2  (visual studio 2005 - windows forms project) and same trouble ocurred with me.

The solution was change the Plataform Target, from "Any CPU" to "X86". (Projet -> Properties -> Build).

Works like a charm too :)

6 people found this reply helpful

·

Was this reply helpful?

Sorry this didn't help.

Great! Thanks for your feedback.

How satisfied are you with this reply?

Thanks for your feedback, it helps us improve the site.

How satisfied are you with this reply?

Thanks for your feedback.

 
 

Question Info


Last updated January 21, 2023 Views 43,368 Applies to: