Search code examples
vb.nettext-to-speechspeech-synthesis

Microsoft Speech.Synthesis Async speech not Asynchronous


Good evening. I'm trying to do a reverb effect using a simple delay, but code does not continue to execute despite using the SpeakAsync Method.

I could find very little documentation on this issue anywhere, so I'd greatly appreciate any tips one can give. Thank you for your time! I'm using System.Speech version 4.0.0 in Visual Studio 2017. I'm targeting runtime 4.5.2

Imports System.Speech.Synthesis
Imports System.Threading
Imports System.Threading.Tasks

Module Module1

Sub main()
    Dim SpeechString As String = "This is a test phrase, there are many 
like it, but this one is mine."
    Call OutSpeech(1, 100, SpeechString)

End Sub


Sub OutSpeech(SpeechRate As Integer, SpeechVolume As Integer, SpeechText As String)
    Dim SpeechHolder As New SpeechSynthesizer

    SpeechHolder.Rate = SpeechRate
    SpeechHolder.Volume = SpeechVolume
    SpeechHolder.SpeakAsync(SpeechText)
    Thread.Sleep(100)
    SpeechHolder.SpeakAsync(SpeechText)
    Console.ReadLine()
End Sub

End Module


Solution

  • Calling SpeechHolder.SpeakAsync(SpeechText) sequentially will just queue the output and the Speech(es) won't overlap.

    A reverb effect is a sort of echo effect where fast waves are merged together in a short a delay. So, to have a reverb-like effect, generate two or more identical sounds with a delay between each of them.

    The Reverb() method will call twice OutSpeech(), setting an adeguate delay (100ms seems appropriate to get the result).

    Sub Reverb()
      Dim Delay As Integer = 100
      Dim SpeechString As String = "This is a test phrase, there are many like it, but this one is mine."
      OutSpeech(1, 100, Delay, SpeechString)
      OutSpeech(1, 100, Delay, SpeechString)
    
    End Sub
    

    The OutSpeech() method becomes an async one, so the calls will overlap when creating a new Synthetizer.
    Two Tasks are created. One to set the Delay, and one to (a)wait while the synthetizer is "speaking", testing SpeechHolder.State.

    Async Sub OutSpeech(SpeechRate As Integer, SpeechVolume As Integer, Delay As Integer, SpeechText As String)
        Using SpeechHolder As SpeechSynthesizer = New SpeechSynthesizer
            SpeechHolder.Rate = SpeechRate
            SpeechHolder.Volume = SpeechVolume
    
            Await Task.Run(Async Function() As Task(Of Boolean)
                           SpeechHolder.SpeakAsync(SpeechText)
                           Await Task.Delay(Delay)
                           Await Task.Run(Sub()
                                              While SpeechHolder.State = SynthesizerState.Speaking
                                              End While
                                          End Sub)
                           Return True
                       End Function)
        End Using
    End Sub