Direct Text-To-Speech - Simultaneous Voices?
Code:
Private V As SpeechLib.SpVoice
Private Sub Command1_Click()
V.Speak (Text1.Text)
End Sub
Private Sub Form_Load()
Set V = New SpeechLib.SpVoice
End Sub
Is there a way to incorporate text-to-speech with multiple voices talking at the same time? This code, along with others I've found, seem to force the application to wait for the speech to finish before speaking again.
Re: Direct Text-To-Speech - Simultaneous Voices?
Hello,
This is a quick answer and just an idea, tested on Win7, it works.
The key is to using separate speech objects with asynchronous speaking.
You might enhance the idea for your need, may be with events.
Code:
Option Explicit
Private spv1 As SpeechLib.SpVoice
Private spv2 As SpeechLib.SpVoice
Private Sub Command1_Click()
Set spv1 = New SpVoice
Set spv1.Voice = spv1.GetVoices.Item(0) 'set your first voice
spv1.Speak "Hello, Did you hear the second voice too?", SVSFlagsAsync
Set spv2 = New SpVoice
Set spv2.Voice = spv2.GetVoices.Item(0) 'set your second voice
spv2.Speak "Hi, This is the second voice", SVSFlagsAsync
End Sub
Re: Direct Text-To-Speech - Simultaneous Voices?
Quote:
Originally Posted by
PGBSoft
Hello,
This is a quick answer and just an idea, tested on Win7, it works.
The key is to using separate speech objects with asynchronous speaking.
You might enhance the idea for your need, may be with events.
Code:
Option Explicit
Private spv1 As SpeechLib.SpVoice
Private spv2 As SpeechLib.SpVoice
Private Sub Command1_Click()
Set spv1 = New SpVoice
Set spv1.Voice = spv1.GetVoices.Item(0) 'set your first voice
spv1.Speak "Hello, Did you hear the second voice too?", SVSFlagsAsync
Set spv2 = New SpVoice
Set spv2.Voice = spv2.GetVoices.Item(0) 'set your second voice
spv2.Speak "Hi, This is the second voice", SVSFlagsAsync
End Sub
This doesn't do what I need. This code speaks one after the other. It does not speak the text simultaneously, as is needed.
Re: Direct Text-To-Speech - Simultaneous Voices?
Hi,
Which OS are you using?
It works as expected on Windows 7, which means, while speaking in voice1 you can hear the voice2 also.
However, no idea how this works on Windows 10.
EDIT:
Can someone confirm that the code in #2 works as expected on Windows7?
Re: Direct Text-To-Speech - Simultaneous Voices?
Quote:
Originally Posted by
PGBSoft
Hi,
Which OS are you using?
It works as expected on Windows 7, which means, while speaking in voice1 you can hear the voice2 also.
However, no idea how this works on Windows 10.
EDIT:
Can someone confirm that the code in #2 works as expected on Windows7?
I am using Windows 10.
Re: Direct Text-To-Speech - Simultaneous Voices?
I don't know if it is Windows Version specific, or SAPI version specific, but I also have Windows 10, and they did not speak simultaneously.
The interaction is asynchronous with the Gui, but the speech method calls get queued up and speak in turn.
I modified the code a little for a test.
Code:
Private spv1 As New SpeechLib.SpVoice
Private spv2 As New SpeechLib.SpVoice
Private Sub Command1_Click()
spv1.Speak "Hello, Did you hear the second voice too?", SVSFlagsAsync
spv2.Speak "Hi, This is the second voice", SVSFlagsAsync
End Sub
Private Sub Form_Load()
Set spv1.Voice = spv1.GetVoices.Item(0) 'set your first voice
Set spv2.Voice = spv2.GetVoices.Item(1) 'set your second voice
'End Sub
So, I click the button and I expect a male voice to speak the first phrase, and a female voice to speak the second phrase.
If I press the button three times in quick succession, I would expect the voices to be interlaced, repeating three times.
What I get is the first voice repeats its phrase three times, followed by the second voice repeating its phrase three times. None of the voices overlap each other.
Re: Direct Text-To-Speech - Simultaneous Voices?
I guess if you use the speech to Stream capability to save the speech to a file, you can then use mci methods, or other methods, to play the two files at the same time.
Code:
Option Explicit
Private Declare Function mciSendString Lib "winmm.dll" _
Alias "mciSendStringA" _
(ByVal lpstrCommand As String, _
ByVal lpstrReturnString As String, _
ByVal uReturnLength As Long, _
ByVal hwndCallback As Long) As Long
Private M As SpeechLib.SpVoice 'M is a male voice
Private F As SpeechLib.SpVoice 'F is a female voice
Private S As SpeechLib.SpFileStream
Private Sub Command1_Click()
mciSendString "Close wav1", 0, 0, 0
mciSendString "Close wav2", 0, 0, 0
'Build a local file path and open it as a stream
Set S = New SpFileStream
Call S.Open("C:\c\SpkStrm1.wav", SSFMCreateForWrite, False)
'Female voice speaks into the file stream and creates a WAV file
Set F.AudioOutputStream = S
F.Speak "Hi, This is the second voice", SVSFNLPSpeakPunc
S.Close
Set S = New SpFileStream
Call S.Open("C:\c\SpkStrm2.wav", SSFMCreateForWrite, False)
'Male voice speaks into a stream and creates a WAV file
Set M.AudioOutputStream = S
M.Speak "i will now speak over top the female voice."
S.Close
mciSendString "Open C:\c\SpkStrm1.wav alias wav1", 0, 0, 0
mciSendString "Open C:\c\SpkStrm2.wav alias wav2", 0, 0, 0
mciSendString "play wav1", 0, 0, 0
mciSendString "play wav2", 0, 0, 0
End Sub
Private Sub Form_Load()
'Create voices
Set F = New SpVoice
Set F.Voice = F.GetVoices("gender=female").Item(0)
Set M = New SpVoice
Set M.Voice = M.GetVoices("gender=male").Item(0)
End Sub
Private Sub Form_Unload(Cancel As Integer)
mciSendString "Close wav1", 0, 0, 0
mciSendString "Close wav2", 0, 0, 0
End Sub
Re: Direct Text-To-Speech - Simultaneous Voices?
Very nice work-around, Passel! I'll leave this thread open for a day or two in case someone has a different idea. Thanks!
Re: Direct Text-To-Speech - Simultaneous Voices?
Quote:
Originally Posted by
mcoulter876
This doesn't do what I need. This code speaks one after the other. It does not speak the text simultaneously, as is needed.
Hello,
How about the following then?
Code:
Option Explicit
Private spv1 As SpeechLib.SpVoice
Private spv2 As SpeechLib.SpVoice
Private Sub Form_Load()
Set spv1 = New SpVoice
Set spv2 = New SpVoice
spv2.Priority = SVPOver
Set spv1.Voice = spv1.GetVoices.Item(0) 'male voice
Set spv2.Voice = spv2.GetVoices.Item(1) 'female voice
End Sub
Private Sub Command1_Click()
spv1.Speak "Hello, First voice goes here. Did you hear the second voice too?", SVSFlagsAsync
spv2.Speak "Hi, This is second voice", SVSFlagsAsync
End Sub
Re: Direct Text-To-Speech - Simultaneous Voices?
Quote:
Originally Posted by
PGBSoft
Hello,
How about the following then?
Code:
Option Explicit
Private spv1 As SpeechLib.SpVoice
Private spv2 As SpeechLib.SpVoice
Private Sub Form_Load()
Set spv1 = New SpVoice
Set spv2 = New SpVoice
spv2.Priority = SVPOver
Set spv1.Voice = spv1.GetVoices.Item(0) 'male voice
Set spv2.Voice = spv2.GetVoices.Item(1) 'female voice
End Sub
Private Sub Command1_Click()
spv1.Speak "Hello, First voice goes here. Did you hear the second voice too?", SVSFlagsAsync
spv2.Speak "Hi, This is second voice", SVSFlagsAsync
End Sub
That did it. But how could I create an array of them, so that it's not needed to specify just 2? That way, it would keep creating new voices each time I clicked the command button. Example: When I quickly click the Command Button 5 times, it creates 5 new voices, all speaking over each other.
1 Attachment(s)
Re: Direct Text-To-Speech - Simultaneous Voices?
Quote:
Originally Posted by
mcoulter876
That did it. But how could I create an array of them, so that it's not needed to specify just 2? That way, it would keep creating new voices each time I clicked the command button. Example: When I quickly click the Command Button 5 times, it creates 5 new voices, all speaking over each other.
Then, you should experiment with the stuff bit.
I don't know what's your actual idea behind this. However, just packing the stuff into a usercontrol and use it appropriately would be a better solution. Another would be to directly use a class module with the stuff, but may have problems with asynchronous speaking.
The attached one is a small demonstration on the usercontrol approach, which you might adopt for your need. Just tested on both Win7 and Win10 and seems working well.
Re: Direct Text-To-Speech - Simultaneous Voices?
Quote:
Originally Posted by
PGBSoft
Then, you should experiment with the stuff bit.
I don't know what's your actual idea behind this. However, just packing the stuff into a usercontrol and use it appropriately would be a better solution. Another would be to directly use a class module with the stuff, but may have problems with asynchronous speaking.
The attached one is a small demonstration on the usercontrol approach, which you might adopt for your need. Just tested on both Win7 and Win10 and seems working well.
@PGBSoft: Is there any way to have the control unload itself once it has finished reading, instead of having to hit the "Reset" button?
Re: Direct Text-To-Speech - Simultaneous Voices?
Quote:
Originally Posted by
mcoulter876
Is there any way to have the control unload itself once it has finished reading, instead of having to hit the "Reset" button?
Hello,
Yes, that's totally possible, if you consider the events of speech object. Just raise your own event at the completion of speaking and unload the UC in the event handler with its index and update the counter. I didn't consider such stuff since this is just a demo.
However, I suggest you to try yourself first. If you can't, just let me know; I will try to give you an update.
Re: Direct Text-To-Speech - Simultaneous Voices?
Quote:
Originally Posted by
PGBSoft
Hello,
Yes, that's totally possible, if you consider the events of speech object. Just raise your own event at the completion of speaking and unload the UC in the event handler with its index and update the counter. I didn't consider such stuff since this is just a demo.
However, I suggest you to try yourself first. If you can't, just let me know; I will try to give you an update.
I think it's in the DoSpeak function. I've placed
Code:
Unload Form1.UserControl11
before it Exits the Sub, but I'm not sure how I would know if it actually unloaded it or not or if that's even what I should be doing. I've never created my own control before and haven't a clue where to even begin raising an event to unload the specific array.
1 Attachment(s)
Re: Direct Text-To-Speech - Simultaneous Voices?
Quote:
Originally Posted by
mcoulter876
I think it's in the DoSpeak function. I've placed
Code:
Unload Form1.UserControl11
You can not unload the UC even with its index inside the DoSpeak since you use asynchronous reading. You need to consider the event raised by the Speech object at the end of the stream and unload the UC in its event-handler. But, better to raise your own event here and handle it within the Form so that you can control the array index and the counter easily.
An update is attached. Now, you can click the button any number of times in a row to produce voices.
1 Attachment(s)
Re: Direct Text-To-Speech - Simultaneous Voices?
You can also do this with a naked Class rather than a UserControl with a little fiddling. While you can't have a WithEvents array we can easily use a late-bound callback method instead. Or if you create a callback Class then your hosting objects such as Forms could implement that if you want early binding.
Form1.frm:
Code:
Option Explicit
Private Const MAX_VOICES As Integer = 4
'Create empty but valid array:
Private Declare Function VoiceArray Lib "oleaut32" Alias "SafeArrayCreateVector" ( _
Optional ByVal VT As VbVarType = vbObject, _
Optional ByVal LB As Long = 0, _
Optional ByVal cElements As Long = 0) As Voice()
Private Voices() As Voice
Public Sub EndStream(ByVal Index As Integer)
Debug.Print "Voice"; Index; "EndStream"
Command1.Enabled = True
End Sub
Private Sub Command1_Click()
Dim I As Integer
For I = 0 To UBound(Voices)
If Voices(I).Idle Then Exit For
Next
If I > UBound(Voices) Then
ReDim Preserve Voices(I)
Set Voices(I) = New Voice
Voices(I).Setup I, Me
Command1.Enabled = I + 1 < MAX_VOICES
End If
Debug.Print "Voice"; I; "Speaking"
Voices(I).Speak "Scooby-Dooby-Doo, where are you? We got some work to do now. " _
& "Scooby-Dooby-Doo, where are you? We need some help from you now."
End Sub
Private Sub Form_Load()
Voices = VoiceArray()
End Sub
Voice.cls:
Code:
Option Explicit
Private Host As Object
Private WithEvents SpVoice As SpeechLib.SpVoice
Private mIdle As Boolean
Private mIndex As Integer
Public Property Get Idle() As Boolean
Idle = mIdle
End Property
Public Property Get Index() As Integer
Index = mIndex
End Property
Public Sub Setup(ByVal NewIndex As Integer, ByVal CallbackHost As Object)
mIndex = NewIndex
'Just in case somebody assigns this more than once we check:
If SpVoice Is Nothing Then
Set SpVoice = New SpeechLib.SpVoice
With SpVoice.GetVoices()
Set SpVoice.Voice = .Item(NewIndex Mod .Count)
End With
SpVoice.Priority = SVPOver
SpVoice.Rate = -2
End If
Set Host = CallbackHost
End Sub
Public Sub Speak(ByVal Text As String)
mIdle = False
SpVoice.Speak Text, SVSFlagsAsync
End Sub
Private Sub Class_Initialize()
mIdle = True
End Sub
Private Sub Class_Terminate()
Set SpVoice = Nothing
Set Host = Nothing
End Sub
Private Sub SpVoice_EndStream(ByVal StreamNumber As Long, ByVal StreamPosition As Variant)
mIdle = True
Host.EndStream mIndex
End Sub