6

可以让 SpeechSynthesizer 以异步方式朗读文本,例如:

Private WithEvents _Synth As New SpeechSynthesizer

Private Sub TextBox1_KeyUp(sender As Object, e As KeyEventArgs) Handles TextBox1.KeyUp
    If e.KeyCode = Keys.Enter Then
        _Synth.SpeakAsync(New Prompt(Me.TextBox1.Text))
    End If
End Sub

生成的事件SpeechSynthesizer使我们能够分辨出计算机语音刚刚在说什么。

例如,您可以通过选择如下字符来可视化语音输出:

Private Sub _Synth_SpeakProgress(sender As Object, e As SpeakProgressEventArgs) Handles _Synth.SpeakProgress

    Me.TextBox1.SelectionStart = e.CharacterPosition
    Me.TextBox1.SelectionLength = e.CharacterCount

End Sub

但是,当SpeakAsync被重复调用时(例如,当我们告诉SpeechSyntesizer说相同的文本而它当前正在说话时),语音请求会排队,并SpeechSynthesizer一个接一个地播放它们。

但是,我无法找出合成器当前正在发出的请求。SpeakProgressEventArgs不要透露这一点

使用 SAPI5,事件提供了StreamNumber

Parameters
StreamNumber
    The stream number which generated the event. When a voice enqueues more than one stream by speaking asynchronously, the stream number is necessary to associate an event with the appropriate stream.

使用这个 StreamNumber,您总是可以知道 SpeechSynthesizer 正在播放/说话的内容。

System.Speech.Synthesis 实现是 SAPI5 实现的现代版本。

但是,我只是没有找到 StreamNumber 指标或类似信息。

System.Speech.Synthesis 提供有关刚刚发生的所有事情的信息,因此它不太可能不提供它正在处理的请求的信息。

这怎么可能找回来?

4

2 回答 2

1

为了澄清我关于使用Prompt 类来保存您需要的任何标识状态的评论,请考虑以下内容,其中Prompt保存了对 source 的引用TextBox

Imports System.Speech.Synthesis
Public Class MyPrompt : Inherits Prompt
    Private tbRef As WeakReference(Of TextBox)

    Public Sub New(textBox As TextBox)
        MyBase.New(textBox.Text)
        ' only hold a weak reference to the TextBox
        ' to avoid any disposal issues
        tbRef = New WeakReference(Of TextBox)(textBox)
    End Sub

    Public ReadOnly Property SourceTextBox As TextBox
        Get
            Dim ret As TextBox = Nothing
            tbRef.TryGetTarget(ret)
            Return ret
        End Get
    End Property
End Class

现在您的原始代码可以写成:

Imports System.Speech.Synthesis

Public Class Form1
    Private WithEvents _Synth As New SpeechSynthesizer

    Private Sub TextBox1_KeyUp(sender As Object, e As KeyEventArgs) Handles TextBox1.KeyUp
        If e.KeyCode = Keys.Enter Then
            ' use a custom prompt to store the TextBox
            _Synth.SpeakAsync(New MyPrompt(Me.TextBox1))
        End If
    End Sub

    Private Sub _Synth_SpeakProgress(sender As Object, e As SpeakProgressEventArgs) Handles _Synth.SpeakProgress
        Dim mp As MyPrompt = TryCast(e.Prompt, MyPrompt)
        If mp IsNot Nothing Then
            Dim tb As TextBox = mp.SourceTextBox
            If tb IsNot Nothing Then
                ' set the selection in the source TextBox
                tb.SelectionStart = e.CharacterPosition
                tb.SelectionLength = e.CharacterCount
            End If
        End If
    End Sub

End Class

编辑:

OP 希望将其与SpeakSsmlAsync 方法一起使用。这本身是不可能的,因为该方法Prompt使用Prompt(String, SynthesisTextFormat) 构造函数创建了一个基础,并Prompt在调用SpeechSynthesizer.SpeakAsync(created_prompt).

下面是一个派生Prompt类,它接受 ssml 字符串或PromptBuilder 实例以及整数标识符。新版本的 MyPrompt 使用 ssml 和整数标识符。

Imports System.Speech.Synthesis

Public Class MyPromptV2 : Inherits Prompt
    Public Sub New(ssml As String, identifier As Int32)
        MyBase.New(ssml, SynthesisTextFormat.Ssml)
        Me.Identifier = identifier
    End Sub

    Public Sub New(builder As PromptBuilder, identifier As Int32)
        MyBase.New(builder)
        Me.Identifier = identifier
    End Sub

    Public ReadOnly Property Identifier As Int32
End Class

...

Imports System.Speech.Synthesis

Public Class Form1
    Private WithEvents _Synth As New SpeechSynthesizer

    Private Sub TextBox1_KeyUp(sender As Object, e As KeyEventArgs) Handles TextBox1.KeyUp
        If e.KeyCode = Keys.Enter Then
            ' build some ssml from the text
            Dim pb As New PromptBuilder
            pb.AppendText(TextBox1.Text)
            ' use ssml and and integer
            _Synth.SpeakAsync(New MyPrompt(pb.ToXml, 10))
            ' or 
            '_Synth.SpeakAsync(New MyPrompt(pb, 10))
        End If
    End Sub

    Private Sub _Synth_SpeakProgress(sender As Object, e As SpeakProgressEventArgs) Handles _Synth.SpeakProgress
        Dim mp As MyPromptV2 = TryCast(e.Prompt, MyPromptV2)
        If mp IsNot Nothing Then
            Select Case mp.Identifier
                Case 10
                    TextBox1.SelectionStart = e.CharacterPosition
                    TextBox1.SelectionLength = e.CharacterCount
            End Select
        End If
    End Sub
End Class
于 2019-04-12T13:55:34.763 回答
0

还有另一种方法可以获取当前正在处理的句子。您可以为您的句子分配选择编号,然后您可以通过获取该句子的索引来识别语音;您可以进一步处理条件。使用方法的SpeechRecognizedEventArgs参数SpeechRecognized获取句子索引。

void sre_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
{
  string txt = e.Result.Text;
  int sentenceIndex = txt.IndexOf("My Sentence");

  if (sentenceIndex >= 0)
  {
    Console.WriteLine("Currently Speaking Sentence: My Sentence, with index number: " 
                 + sentenceIndex);
  }

  //.... some code here
}

在此处遵循完整示例。


编辑1:

类范围SpeechSynthesizer对象使应用程序能够说话。该SpeechRecognitionEngine对象允许应用程序收听和识别口语单词或短语。

于 2019-04-11T14:25:06.643 回答