Где находится номер потока в информации о событии для System.Speech.Synthesis?

Можно позволить 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 предоставляет информацию обо всем, что только что происходит, поэтому маловероятно, что он не предоставляет информацию о том, какой из запросов он просто обрабатывает.

Как это можно было восстановить?


person tmighty    schedule 02.04.2019    source источник
comment
Почему вы не можете отследить это через аргумент Prompt SpeakAsync? SpeakProgressEventArgs включает Prompt, как и SpeakCompletedEventArgs.   -  person TnTinMn    schedule 11.04.2019
comment
@TnTinMn Я пробовал это, но не нашел способа отличить одно приглашение от другого, если текст обоих одинаковый. Итак, если вы кормите 5 Hello! запрашивает SpeechSynthesizer, все 5 выглядят одинаково в событии SpeechProgress. Что ты об этом думаешь?   -  person tmighty    schedule 12.04.2019
comment
Класс приглашения не запечатан, поэтому вы можете создать производный класс (Inherits Prompt) со всеми нужными вам идентифицирующими свойствами и передать его в SpeakAsync.   -  person TnTinMn    schedule 12.04.2019


Ответы (2)


Чтобы пояснить мой комментарий об использовании Класс подсказки для хранения любого необходимого вам состояния идентификации, рассмотрите следующее, где Prompt содержит ссылку на источник 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

Редактировать:

ОП хочет использовать это с метод 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
person TnTinMn    schedule 12.04.2019
comment
Ваш ответ действительно элегантен. Не могли бы вы показать, как можно ввести целое число (для идентификатора) в класс MyPrompt и НЕ использовать текстовое поле? Я пытался это сделать, но он не скомпилировался, так как _Synth.SpeakSsmlAsync не принимал MyPrompt. Моя строка была _Synth.SpeakSsmlAsync(New MyPrompt(iMyNewID, MyStringBuilderToCompileSsml.ToString)). - person tmighty; 12.04.2019
comment
@tmighty, SpeakSsmlAsync - это просто вспомогательный метод, который создает Prompt с помощью конструктора New Prompt(textToSpeak, SynthesisTextFormat.Ssml), а затем вызывает SpeakAsync(prompt) . Невозможно изменить Prompt, который создает этот метод. В MyPrompt можно добавить перегрузку конструктора, чтобы принять ssml, а также ваше целочисленное значение. Я отредактирую сообщение в ближайшее время. - person TnTinMn; 12.04.2019
comment
OMG, большое спасибо за ваш глубокий ответ. Ты запрограммировал эту часть фреймворка или ты просто монстр? :-) - person tmighty; 12.04.2019
comment
@tmighty, нет, я не виноват в фреймворке; Я думаю, что остается второй вариант, который вы предоставили. :) Однако я предпочитаю термин огр. - person TnTinMn; 12.04.2019

Есть еще один способ узнать, какое предложение обрабатывается в данный момент. Вы можете присвоить своим предложениям номера выбора, а затем распознать речь, получив индекс этого предложения; которые вы можете дополнительно обработать условия. Используйте аргумент 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:

Объект class-scope SpeechSynthesizer дает приложению возможность говорить. Объект SpeechRecognitionEngine позволяет приложению прослушивать и распознавать произносимые слова или фразы.

person 5377037    schedule 11.04.2019
comment
Спасибо, но мой вопрос касается SpeechSynthesis, а не SpeechRecognition. - person tmighty; 11.04.2019
comment
Вы следовали полному примеру? См. Редактировать. - person 5377037; 11.04.2019
comment
Да, я сделал, в этой статье с полным примером не было ничего, что могло бы способствовать моему вопросу. Кроме того, я думаю, вы сбиваете с толку то, что делает int OfferingIndex в приведенном вами примере. Но все равно спасибо. - person tmighty; 11.04.2019