Непрерывная диктовка

Узнайте, как записать и распознать длительный непрерывный речевой ввод.

Важные API-интерфейсы: SpeechContinuousRecognitionSession, ContinuousRecognitionSession

В разделе Распознавание речи вы узнали, как записать и распознать относительно короткий речевой ввод с помощью методов RecognizeAsync или RecognizeWithUIAsync объекта SpeechRecognizer, например при составлении сообщения SMS или вопроса.

Чтобы записывать более длительные сеансы распознавания речи, например диктовку или сообщение электронной почты, используйте свойство ContinuousRecognitionSessionSpeechRecognizer для получения объекта SpeechContinuousRecognitionSession.

Примечание

Поддержка языка диктовки зависит от устройства , на котором выполняется приложение. Для компьютеров и ноутбуков распознается только en-US, а Xbox и телефоны могут распознавать все языки, поддерживаемые распознаванием речи. Дополнительные сведения см. в разделе Указание языка распознавателя речи.

Настройка

Для управления непрерывным сеансом диктовки вашему приложению понадобятся несколько объектов:

  • экземпляр объекта SpeechRecognizer;
  • ссылка на диспетчера пользовательского интерфейса для обновления пользовательского интерфейса во время диктовки;
  • способ отслеживания всей совокупности слов, сказанных пользователем.

Здесь мы объявляем экземпляр SpeechRecognizer как закрытое поле класса кода программной части. Ваше приложение должно сохранить ссылку в любом другом месте, если непрерывная диктовка должна продолжиться дальше отдельной XAML-страницы.

private SpeechRecognizer speechRecognizer;

Во время диктовки распознаватель создает события из фонового потока. Так как фоновый поток не может напрямую обновлять пользовательский интерфейс в XAML, ваше приложение должно использовать диспетчер для обновления пользовательского интерфейса в ответ на события распознавания.

Здесь мы объявляем закрытое поле, которое будет инициализировано позже с помощью диспетчера пользовательского интерфейса.

// Speech events may originate from a thread other than the UI thread.
// Keep track of the UI thread dispatcher so that we can update the
// UI in a thread-safe manner.
private CoreDispatcher dispatcher;

Для отслеживания речи пользователя необходимо обрабатывать события распознавания, которые создаются распознавателем речи. Эти события предоставляют результаты распознавания отрывков реплик пользователя.

Здесь мы используем объект StringBuilder, чтобы сохранить все предыдущие результаты распознавания, полученные в течение сеанса. Новые результаты добавляются в StringBuilder по мере обработки.

private StringBuilder dictatedTextBuilder;

Инициализация

Во время инициализации непрерывного распознавания речи необходимо выполнить следующие действия.

  • Получить диспетчер для потока пользовательского интерфейса, если пользовательский интерфейс приложения обновляется в обработчиках событий непрерывных речи.
  • Инициализировать распознаватель речи.
  • Скомпилировать встроенную грамматику диктовки. Примечание Для распознавания речи требуется по крайней мере одно ограничение для определения узнаваемого словаря. Если не задано ни одно ограничение, будет использоваться предопределенная грамматика речевого ввода. См. Распознавание речи.
  • Настроить прослушиватели событий распознавания.

В данном примере мы инициализируем распознавание речи в событии страницы OnNavigatedTo.

  1. Поскольку события, создаваемые распознавателем речи, возникают в фоновом потоке, создайте ссылку на диспетчер для обновления потока пользовательского интерфейса. OnNavigatedTo всегда вызывается в потоке пользовательского интерфейса.
this.dispatcher = CoreWindow.GetForCurrentThread().Dispatcher;
  1. Затем мы инициализируем экземпляр SpeechRecognizer.
this.speechRecognizer = new SpeechRecognizer();
  1. Затем мы добавляем и компилируем грамматику, которая определяет все слова и фразы, распознаваемые SpeechRecognizer.

    Если вы не укажете грамматику явным образом, по умолчанию используется предопределенная грамматика для диктовки. Как правило, грамматика по умолчанию лучше всего подходит для общей диктовки.

    В данном случае мы сразу вызываем CompileConstraintsAsync без добавления грамматики.

SpeechRecognitionCompilationResult result =
      await speechRecognizer.CompileConstraintsAsync();

Обработка событий распознавания

Можно записать отдельную краткую реплику или фразу путем вызова RecognizeAsync или RecognizeWithUIAsync.

А чтобы записать более длительный сеанс непрерывного распознавания, мы определяем прослушиватели событий для работы в фоновом режиме во время речи пользователя, а также определяем обработчики для формирования строки диктовки.

Затем мы используем свойство ContinuousRecognitionSession нашего распознавателя, чтобы получить объект SpeechContinuousRecognitionSession, который предоставляет методы и события для управления сеансом непрерывного распознавания.

Два события являются особенно важными.

  • ResultGenerated, которое возникает, когда распознаватель сформировал некоторые результаты.
  • Completed, которое возникает при завершении сеанса непрерывного распознавания.

Событие ResultGenerated возникает, когда пользователь говорит. Распознаватель непрерывно слушает пользователя и периодически вызывает событие, которое передает реплики речевого ввода. Необходимо изучить речевой ввод с помощью свойства Result аргумента события и принять необходимые меры в обработчике событий, например добавить текст в объект StringBuilder.

В качестве экземпляра SpeechRecognitionResult свойство Result полезно для определения необходимости принятия речевого ввода. SpeechRecognitionResult предоставляет два таких свойства:

  • Status: указывает, было ли распознавание успешным. Распознавание может завершиться с ошибкой по различным причинам.
  • Confidence: указывает относительную уверенность в том, что распознаватель понял правильные слова.

Далее описаны основные действия, обеспечивающие поддержку непрерывного распознавания.

  1. Здесь мы регистрируем обработчик для события непрерывного распознавания ResultGenerated в событии страницы OnNavigatedTo.
speechRecognizer.ContinuousRecognitionSession.ResultGenerated +=
        ContinuousRecognitionSession_ResultGenerated;
  1. Затем мы проверяем свойство Confidence. Если значение уверенности равно Medium или выше, мы добавляем текст в StringBuilder. Также мы обновляем пользовательский интерфейс по мере сбора ввода.

    Обратите внимание , что событие ResultGenerated возникает в фоновом потоке, который не может обновить пользовательский интерфейс напрямую. Если обработчику необходимо обновить пользовательский интерфейс (как это делает [пример службы распознавания речи и TTS]), необходимо отправить обновления в поток пользовательского интерфейса с помощью метода RunAsync диспетчера.

private async void ContinuousRecognitionSession_ResultGenerated(
      SpeechContinuousRecognitionSession sender,
      SpeechContinuousRecognitionResultGeneratedEventArgs args)
      {

        if (args.Result.Confidence == SpeechRecognitionConfidence.Medium ||
          args.Result.Confidence == SpeechRecognitionConfidence.High)
          {
            dictatedTextBuilder.Append(args.Result.Text + " ");

            await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
              dictationTextBox.Text = dictatedTextBuilder.ToString();
              btnClearText.IsEnabled = true;
            });
          }
        else
        {
          await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
              dictationTextBox.Text = dictatedTextBuilder.ToString();
            });
        }
      }
  1. Затем мы обрабатываем событие Completed, которое указывает на конец непрерывной диктовки.

    Сеанс заканчивается, когда вы вызываете методы StopAsync или CancelAsync (описано в следующем разделе). Сеанс также завершается, когда возникает ошибка или когда пользователь перестает говорить. Проверьте свойство Status аргумента события, чтобы определить причину завершения сеанса (SpeechRecognitionResultStatus).

    Здесь мы регистрируем обработчик для события непрерывного распознавания Completed в событии страницы OnNavigatedTo.

speechRecognizer.ContinuousRecognitionSession.Completed +=
      ContinuousRecognitionSession_Completed;
  1. Обработчик событий проверяет свойства Status, чтобы определить, было ли распознавание успешным. Он также обрабатывает те случаи, когда пользователь перестает говорить. Часто TimeoutExceeded считается успешным распознаванием, так как это означает, что пользователь перестал говорить. Вы должны обрабатывать эти случаи в своем коде, обеспечивая удобную работу пользователя.

    Обратите внимание , что событие ResultGenerated возникает в фоновом потоке, который не может обновить пользовательский интерфейс напрямую. Если обработчику необходимо обновить пользовательский интерфейс (как это делает [пример службы распознавания речи и TTS]), необходимо отправить обновления в поток пользовательского интерфейса с помощью метода RunAsync диспетчера.

private async void ContinuousRecognitionSession_Completed(
      SpeechContinuousRecognitionSession sender,
      SpeechContinuousRecognitionCompletedEventArgs args)
      {
        if (args.Status != SpeechRecognitionResultStatus.Success)
        {
          if (args.Status == SpeechRecognitionResultStatus.TimeoutExceeded)
          {
            await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
              rootPage.NotifyUser(
                "Automatic Time Out of Dictation",
                NotifyType.StatusMessage);

              DictationButtonText.Text = " Continuous Recognition";
              dictationTextBox.Text = dictatedTextBuilder.ToString();
            });
          }
          else
          {
            await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
              rootPage.NotifyUser(
                "Continuous Recognition Completed: " + args.Status.ToString(),
                NotifyType.StatusMessage);

              DictationButtonText.Text = " Continuous Recognition";
            });
          }
        }
      }

Отображение реакции в процессе распознавания

Когда люди беседуют, они, как правило, полагаются на контекст, чтобы полностью понять произносимое. Аналогичным образом распознаватель речи часто нуждается в контексте, чтобы обеспечить высокодостоверные результаты распознавания. Например, сами по себе слова «кот» и «код» неразличимы, пока за счет других слов в предложении или фразе не будет понятен контекст. Пока распознаватель не получит определенной степени уверенности, что слово или слова были распознаны надлежащим образом, событие ResultGenerated не будет создано.

Это может привести к некоторым неудобствам для пользователя, так как он будет продолжать говорить, но не увидит результата, пока степень уверенности в распознавателе не будет достаточной для вызова события ResultGenerated.

Для исправления этого кажущегося отсутствия какой-либо реакции следует обработать событие HypothesisGenerated. Это событие возникает, когда распознаватель создает новый набор потенциальных соответствий для обрабатываемого слова. Аргумент события предоставляет свойство Hypothesis, содержащее текущие соответствия. Покажите это пользователю по мере того, как он продолжает говорить, чтобы уверить его в том, что обработка все еще продолжается. После того как уверенность будет достаточно высокой и результат распознавания будет определен, замените промежуточные результаты Hypothesis окончательным результатом Result, предоставленным в событии ResultGenerated.

Здесь мы добавляем гипотетический текст и многоточие (...) в текущее значение поля вывода TextBox. Содержимое текстового поля обновляется по мере формирования новых гипотез и до получения окончательных результатов из события ResultGenerated.

private async void SpeechRecognizer_HypothesisGenerated(
  SpeechRecognizer sender,
  SpeechRecognitionHypothesisGeneratedEventArgs args)
  {

    string hypothesis = args.Hypothesis.Text;
    string textboxContent = dictatedTextBuilder.ToString() + " " + hypothesis + " ...";

    await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
      dictationTextBox.Text = textboxContent;
      btnClearText.IsEnabled = true;
    });
  }

Запуск и остановка распознавания

Перед запуском сеанса распознавания проверьте значение свойства State распознавателя речи. Распознаватель речи должен находиться в состоянии Idle.

После проверки состояния распознавателя речи мы начинаем сеанс, вызвав метод StartAsync свойства ContinuousRecognitionSession распознавателя речи.

if (speechRecognizer.State == SpeechRecognizerState.Idle)
{
  await speechRecognizer.ContinuousRecognitionSession.StartAsync();
}

Распознавание может быть остановлено двумя способами:

  • StopAsync позволяет завершиться всем ожидающим событиям распознавания речи (ResultGenerated продолжает формироваться, пока все невыполненные операции распознавания не будут завершены).
  • CancelAsync незамедлительно завершает сеанс распознавания речи и отменяет все невыполненные операции.

После проверки состояния распознавателя речи мы останавливаем сеанс, вызвав метод CancelAsync свойства ContinuousRecognitionSession распознавателя речи.

if (speechRecognizer.State != SpeechRecognizerState.Idle)
{
  await speechRecognizer.ContinuousRecognitionSession.CancelAsync();
}

Примечание

Событие ResultGenerated может возникнуть после вызова CancelAsync.
Из-за многопоточности событие ResultGenerated может по-прежнему оставаться в стеке при вызове CancelAsync. В этом случае событие ResultGenerated все равно возникает.
Если заданы какие-либо закрытые поля при отмене сеанса распознавания, всегда проверяйте их значения в обработчике ResultGenerated. Например, не думайте, что поле будет инициализироваться в обработчике, если ему задано значение NULL при отмене сеанса.

 

Примеры