Discussion:
Text to Voice
(too old to reply)
StanWeiss
2024-08-04 16:16:17 UTC
Permalink
ObiWan
Again thanks for your help.

I do my development and testing on an HP Lap Top running Windows 7 Pro.
This morning I had some time and checked out the test application on my
HP Lap Top running Windows 10 and it sounded fine. The Windows 10
machines has 2 voices where as the Windows 7 machine only has 1. I guess
now it is time to add code to let the User select which voice. Right now
I have the program hard coded to Set objSAPI.Voice =
objSAPI.GetVoices.Item(0)

Stan

Stan
--
This email has been checked for viruses by Avast antivirus software.
www.avast.com
ObiWan
2024-08-05 07:55:01 UTC
Permalink
Right now I have the program hard coded to Set
objSAPI.Voice = objSAPI.GetVoices.Item(0)
Well, start by checking if there's ANY installed voice, if not then
give the user a notice and disable all options, otherwise, you may
consider using a collection carrying structures like this one (e.g.)

Type SAPIvoice
nIndex As Long ' index of voice, zero based
sDescr As String ' description
sGender As String ' gender
sAge As String ' age
sLangID As String ' language ID
End Type

then you loop over the voices like in

nVoices = objSAPI.GetVoices.Count
For i=0 To nVoices-1
set objVoice = objSAPI.GetVoices.Item(i)
... see below ...
Next i

and for each voice you'll retrieve the desired informations by using
commands like these

sName= objSAPI.GetVoices.Item(i).GetAttribute("Name")
sGender = objSAPI.GetVoices.Item(i).GetAttribute("Gender")
sAge = objSAPI.GetVoices.Item(i).GetAttribute("Age")
sLangID = objSAPI.GetVoices.Item(i).GetAttribute("Language")

which you can then use to populate the structure members, after which
you'll add the structure to your collection, ad that point it will be
easy to use the collection to feed an array tied to a combobox and
allowing the user to select the desired voice; notice that the language
ID is a hex string, so you may want to decode that to a human readable
language name, a way to do that is to have a builtin table containing
these codes

https://learn.microsoft.com/en-us/openspecs/office_standards/ms-oe376/6c085406-a698-4e12-9d4d-c3b0ee3dbc4a

in hex format (e.g. 409 = 1033 -> English/US) and then using it to turn
the hex code in "Language ID" to a readable string, also notice that
the SAPI can also perform some attributes matching, for example to only
return voices in a given language or for a given gender

https://learn.microsoft.com/en-us/previous-versions/windows/desktop/ee125639(v=vs.85)

as explained above
ObiWan
2024-08-05 07:58:58 UTC
Permalink
just a quick example in VBScript


Dim sMsg, objSAPI
Dim nV, i, oV, iV
Dim sLang

Set objSAPI=CreateObject("SAPI.SPVoice")

nV = objSAPI.GetVoices.Count
iV = 0
For i=0 to nV-1
Set oV = objSAPI.GetVoices.Item(i)
sLang = "&h" & objSAPI.GetVoices.Item(i).GetAttribute("Language")
sMsg = "Name=" & objSAPI.GetVoices.Item(i).GetAttribute("Name") & _
", Gender=" & objSAPI.GetVoices.Item(i).GetAttribute("Gender") & _
", Age=" & objSAPI.GetVoices.Item(i).GetAttribute("Age") & _
", Language=" & Int(sLang)
WScript.StdOut.WriteLine i & ") " & sMsg
If sLang="409" Then
iV = i ' last en-US voice selected
End if
Next

i = iV
Set objSAPI.Voice = objSAPI.GetVoices.Item(i)

objSAPI.Rate = -1
objSAPI.Volume = 100

sMsg="Hello, this is speech API, this is " & _
objSAPI.GetVoices.Item(i).GetAttribute("Name") & " " & _
objSAPI.GetVoices.Item(i).GetAttribute("Gender") & " voice"

objSAPI.Speak sMsg
ObiWan
2024-08-05 08:16:42 UTC
Permalink
Oh... and PLEASE, please, reply in the SAME thread, don't open a new
one for each reply !
StanWeiss
2024-08-05 14:22:56 UTC
Permalink
Post by ObiWan
Oh... and PLEASE, please, reply in the SAME thread, don't open a new
one for each reply !
I just changed my settings. I had it setup to delete messages that were
more than 3 days old. I changed it to 14 days.

Stan
--
This email has been checked for viruses by Avast antivirus software.
www.avast.com
ObiWan
2024-08-05 09:25:32 UTC
Permalink
notice that the language ID is a hex string, so you may want to
decode that to a human readable language name
simpler way

https://learn.microsoft.com/en-us/windows/win32/api/winnls/nf-winnls-lcidtolocalename

:)
ObiWan
2024-08-05 09:49:21 UTC
Permalink
Post by ObiWan
https://learn.microsoft.com/en-us/windows/win32/api/winnls/nf-winnls-lcidtolocalename
an example in "C" language, shouldn't be difficult to translate to VB


#include <stdio.h>
#include <stdlib.h>

#include <windows.h>

#pragma comment(lib,"kernel32.lib")

int main(void)
{
int rc, sz;
LCID id;
LPWSTR pName;


// initialize size to zero and buffer to null
sz = 0;
pName = NULL;

// setup the locale we want to "decode"
id = 1033;

// first call, obtain the needed buffer size
rc = LCIDToLocaleName(id, pName, sz, 0);
if (rc < 0)
{
printf("Error %d\n", rc);
return(1);
}

// allocate the locale string buffer
sz = rc+4;
pName = (LPWSTR)malloc(sz);
if (NULL == pName)
{
printf("Memory error\n");
return(2);
}

// now read the locale name into buffer
rc = LCIDToLocaleName(id, pName, sz, 0);
if (rc < 0)
{
printf("Loc Error %d\n", rc);
return(3);
}

// show the name
printf("%d -> %ws\n", id, pName);

// all ok, cleanup and exit
free(pName);
return(0);
}
StanWeiss
2024-08-05 14:13:05 UTC
Permalink
Post by ObiWan
Right now I have the program hard coded to Set
objSAPI.Voice = objSAPI.GetVoices.Item(0)
Well, start by checking if there's ANY installed voice, if not then
give the user a notice and disable all options, otherwise, you may
consider using a collection carrying structures like this one (e.g.)
Type SAPIvoice
nIndex As Long ' index of voice, zero based
sDescr As String ' description
sGender As String ' gender
sAge As String ' age
sLangID As String ' language ID
End Type
then you loop over the voices like in
nVoices = objSAPI.GetVoices.Count
For i=0 To nVoices-1
set objVoice = objSAPI.GetVoices.Item(i)
... see below ...
Next i
and for each voice you'll retrieve the desired informations by using
commands like these
sName= objSAPI.GetVoices.Item(i).GetAttribute("Name")
sGender = objSAPI.GetVoices.Item(i).GetAttribute("Gender")
sAge = objSAPI.GetVoices.Item(i).GetAttribute("Age")
sLangID = objSAPI.GetVoices.Item(i).GetAttribute("Language")
which you can then use to populate the structure members, after which
you'll add the structure to your collection, ad that point it will be
easy to use the collection to feed an array tied to a combobox and
allowing the user to select the desired voice; notice that the language
ID is a hex string, so you may want to decode that to a human readable
language name, a way to do that is to have a builtin table containing
these codes
https://learn.microsoft.com/en-us/openspecs/office_standards/ms-oe376/6c085406-a698-4e12-9d4d-c3b0ee3dbc4a
in hex format (e.g. 409 = 1033 -> English/US) and then using it to turn
the hex code in "Language ID" to a readable string, also notice that
the SAPI can also perform some attributes matching, for example to only
return voices in a given language or for a given gender
https://learn.microsoft.com/en-us/previous-versions/windows/desktop/ee125639(v=vs.85)
as explained above
Thanks. I have some of that coded already. See below. I will see how
what I have done matches up with what you have posted. After testing
both voices that come with Windows 10 I see that the rate I have that
worked well on Windows 7 and 1 Windows 10 voice doesn't work well with
the other windows 10 voice. So I thinking about adding, letting the User
be able to adjust the rate.

Stan

Attribute VB_Name = "Form1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False

Option Explicit

Dim RECIN As String
Dim WorkString As String
Dim WorkString2 As String
Dim Voice_Count As Integer

Dim sMsg, objSAPI, T

Private Sub Form_Load()     'Start Up code
Me.Left = (Screen.Width - Me.Width) / 2     'Center form on screen
Me.Top = (Screen.Height - Me.Height) / 2
'
sMsg = "Hello World, this is S.A.P.I. that is, Speech API"
Set objSAPI = CreateObject("SAPI.SPVoice")
'
' My Windows 7 Computer only has 1 Voice
'
Voice_Count = objSAPI.GetVoices.Count
MsgBox "Voice Count = " & Voice_Count
Set objSAPI.Voice = objSAPI.GetVoices.Item(0)
'
' Get each token in the collection returned by GetVoices
For Each T In objSAPI.GetVoices
    WorkString = T.GetDescription  'The token's name
    List1.AddItem WorkString  ' Add to listbox
    MsgBox WorkString
Next
'
' The default rate for a voice is set through Speech properties in
Control Panel.
' Values for the Rate property range from -10 to 10, which represent the
slowest
' and the fastest speaking rates, respectively.
'
objSAPI.Rate = -5
objSAPI.Volume = 100
End Sub

Private Sub Form_Unload(Cancel As Integer)  'User Exited by Using Close
    Call Exit_Click
End Sub

Private Sub Exit_Click()      'End Program
Dim FormX As Form
For Each FormX In Forms
    DoEvents
    If FormX.Name <> Me.Name Then
        Unload FormX
        Set FormX = Nothing
    End If
Next FormX
'
Set objSAPI = Nothing
'
Unload Me
End Sub

Private Sub Speak_Click()
' objSAPI.Speak sMsg
  objSAPI.Speak Text1.Text
End Sub

Private Sub Select_Voice_Click()
Frame1.Visible = True
End Sub

Private Sub List1_Click()
On Error GoTo Selection_Problem

If List1.ListIndex > -1 Then

' Set voice object to voice name selected in
' list box then have new voice speaks its own name
'
    Set objSAPI.Voice = objSAPI.GetVoices().Item(List1.ListIndex)
    objSAPI.Speak List1.Text
    Frame1.Visible = False
Else
    MsgBox "Please select a voice from the list box."
End If

Exit Sub

Selection_Problem:
If Err.Number Then
    WorkString2 = "Desc: " & Err.Description & vbNewLine
    WorkString2 = WorkString2 & "Err #: " & Err.Number
    MsgBox WorkString2, vbExclamation, "Run-Time Error"
End If
End Sub

Stan
--
This email has been checked for viruses by Avast antivirus software.
www.avast.com
ObiWan
2024-08-05 14:35:20 UTC
Permalink
:: On Mon, 5 Aug 2024 10:13:05 -0400
:: (microsoft.public.vb.general.discussion)
Post by StanWeiss
Thanks. I have some of that coded already. See below. I will see how
what I have done matches up with what you have posted. After testing
both voices that come with Windows 10 I see that the rate I have that
worked well on Windows 7 and 1 Windows 10 voice doesn't work well
with the other windows 10 voice. So I thinking about adding, letting
the User be able to adjust the rate.
My suggestion is to start by checking if there's at least one voice
installed, if so, allow the user to set the various parameters, those
will include a combo to select the language, a second combo to select
the available voices for that language and then a couple of sliders,
one to set the rate and a second one to set the volume, that should
cover most if not all requirements, allowing the end user to customize
the speech to his/her own taste; by the way, add a "test voice" button
to allow the user to quickly check the changes and ensure they'll be as
(s)he wants, that would be easy, just a textbox to enter some text (it
may be pre-populated with some standard text) and a "test" button to
start the "voice listen" operation

Loading...