Включение поддержки голосовых команд в приложение Windows Phone 8

OSzone.net » Microsoft » Разработка приложений » Windows Phone » Включение поддержки голосовых команд в приложение Windows Phone 8
Автор: Эвери Бишоп
Иcточник: MSDN Magazine
Опубликована: 25.02.2013

Как-то недавно вечером я опаздывал на встречу с одним старым приятелем. Я знал, что он уже едет, поэтому звонить ему было как-то не с руки. Тем не менее, когда я выскочил из своего офиса и бежал к машине, я схватил свой Windows Phone и прижал кнопку Start. Услышав характерный звуковой сигнал (earcon) приглашения к прослушиванию команд, я произнес: «Text Robert Brown»; после запуска текстового приложения, я сказал: «Опаздываю, только что выехал из офиса», а затем произнес команду «Send», чтобы отправить текстовое сообщение.

Без средств речевой поддержки во встроенном текстовом приложении мне пришлось бы остановиться и в спешке тыкать в экран, чтобы отправить текст, потому что мне трудно пользоваться клавиатурой моими толстыми пальцами, а читать экран на бегу весьма проблематично. Функция преобразования речи в текст сэкономила мне время, избавила от заминок и не доставила ни малейших хлопот.

Windows Phone 8 предлагает те же средства для разработчиков, чтобы они могли взаимодействовать со своими пользователями посредством распознавания и синтеза речи. Эти средства поддерживают два сценария применения, проиллюстрированные в моем примере: пользователь смартфона может произнести команду для запуска какого-либо приложения и выполнить некое действие всего одним словом; после запуска приложения смартфон переходит к диалогу с пользователем, захватывая команды или текст из фрагментов активной речи (utterances) и воспроизводя текст из динамика пользователю для уведомления и обратной связи.

Первый сценарий поддерживается средством, которое называется голосовыми командами. Чтобы включить эту функцию, приложение предоставляет файл Voice Command Definition (VCD), где указывает набор команд, которые оно может обрабатывать. Когда приложение запускается голосовыми командами, оно принимает параметры в строке запроса, такие как имя команды, имена параметров и распознанный текст, которые оно может задействовать для выполнения команды, указанной пользователем. В этой статье (первой в серии из двух частей) поясняется, как включить поддержку голосовых команд в вашем приложении в Windows Phone 8.

Во второй части мы обсудим речевой диалог внутри приложения. Для его поддержки Windows Phone 8 предоставляет API, обеспечивающий распознавание и синтез речи. Этот API включает UI по умолчанию для подтверждения и поддержки однозначности, а также значения по умолчанию для речевых грамматик, периоды ожидания и другие свойства, что делает возможным добавление распознавания речи в приложение с помощью всего нескольких строк кода. Аналогично API синтеза речи (также известный как преобразование текста в речь [text-to-speech, TTS]) легко кодируется в простых сценариях; он также предоставляет расширенные средства вроде манипуляций с тонкой настройкой через World Wide Web Consortium Speech Synthesis Markup Language (SSML) и переключение между голосом конечного пользователя, уже записанного смартфоном, и голосами, которые можно скачать из Marketplace. Детальные пояснения этой функциональности будут даны во второй части этой серии.

Чтобы продемонстрировать эти средства, я создал простое приложение Magic Memo. Вы можете запустить его и выполнить какую-нибудь команду, удерживая кнопку Start и произнеся команду после приглашения. В приложении вы можете вводить заметки, используя диктование или навигацию по приложению и выполнение его команд с помощью голосового управления. На протяжении всей статьи я буду пояснять исходный код, реализующий эти возможности.

Требования к использованию речевых средств в приложениях

Приложение Magic Memo должно работать безо всякой подготовки, если ваша среда разработки удовлетворяет программно-аппаратным требованиям для создания приложений Windows Phone 8, а тестирование осуществляется на эмуляторе смартфона. Когда эта статья ушла в печать, требования были следующими:

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

При разработке приложения с нуля следует учитывать еще три вещи.

  1. Убедитесь, что микрофон и динамик устройства работают должным образом.
  2. Добавляйте функциональность распознавания речи и поддержки микрофона в файл WpAppManifest.xml, либо устанавливая соответствующие флажки в редакторе свойств, либо вручную включая в XML-файл следующие строки:
           <Capability Name="ID_CAP_SPEECH_RECOGNITION"/>
           <Capability Name="ID_CAP_MICROPHONE"/>
  3. При использовании распознавания речи нужно захватывать исключение, генерируемое, когда пользователь не принимает политику речевой изолированности (speech privacy policy). Вспомогательная функция GetNewMemoByVoice в MainPage.xaml.cs в сопутствующем исходном коде дает пример того, как это делается.

В приложении вы можете вводить заметки, используя диктование или навигацию по приложению и выполнение его команд с помощью голосового управления.

Сценарий

На любом смартфоне распространенный сценарий заключается в запуске какого-либо приложения и выполнении одной команды, за которой могут (но не обязательно) последовать другие команды. Если делать это вручную, потребуется несколько операций: поиск приложения, навигация в нужное место, поиск соответствующей кнопки или элемента меню, его касание и т. д. Многие пользовали находят это несколько утомительным даже после того, как они привыкают к этим операциям.

Скажем, чтобы увидеть сохраненную заметку (допустим, № 12) в приложении-примере Magic Memo, пользователь должен найти и запустить это приложение, коснуться «View saved memos» и прокручивать экран вниз, пока на нем не появится нужная заметка. В противоположность этому голосовые команды в Windows Phone 8 позволяют добиться той же цели гораздо быстрее: пользователь удерживает кнопку Start и произносит «Magic Memo, show memo 12», после чего происходит запуск приложения Magic Memo и в информационном окне появляется именно эта заметка. Даже в таком простом случае ускорение манипуляций налицо.

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

Определение распознаваемых пользовательских команд

Первый шаг в реализации голосовых команд — определение прослушиваемых команд в VCD-файле. Этот файл создается в простом XML-формате и состоит из набора элементов CommandSet, каждый из которых имеет дочерние элементы Command, а те в свою очередь содержат прослушиваемые фразы. Пример этого файла для приложения Magic Memo показан на рис. 1.

Рис. 1. Файл определений голосовых команд (VCD) для приложения Magic Memo

<?xml version="1.0" encoding="utf-8"?>
<VoiceCommands xmlns="http://schemas.microsoft.com/voicecommands/1.0">
  <CommandSet xml:lang="en-us" Name="MagicMemoEnu">
    <!-- CommandSet для всех команд (US English) -->
    <CommandPrefix>Magic Memo</CommandPrefix>
    <Example>enter a new memo</Example>

    <Command Name="newMemo">
      <Example>enter a new memo</Example>
      <ListenFor>Enter [a] [new] memo</ListenFor>
      <ListenFor>Make [a] [new] memo</ListenFor>
      <ListenFor>Start [a] [new] memo</ListenFor>
      <Feedback>Entering a new memo</Feedback>
      <Navigate /> <!-- по умолчанию - на страницу Main -->
    </Command>

    <Command Name="showOne">
      <Example>show memo number two</Example>
      <ListenFor>show [me] memo [number] {num} </ListenFor>
      <ListenFor>display memo [number] {num}</ListenFor>
      <Feedback>Showing memo number {num}</Feedback>
      <Navigate Target="/ViewMemos.xaml"/>
    </Command>

    <PhraseList Label="num">
      <Item> 1 </Item>
      <Item> 2 </Item>
      <Item> 3 </Item>
    </PhraseList>
  </CommandSet>

  <CommandSet xml:lang="ja-JP" Name="MagicMemoJa">
    <!-- CommandSet для всех команд на японском -->
    <CommandPrefix>マジック・メモ</CommandPrefix>
    <Example>新規メモ</Example>

    <Command Name="newMemo">
      <Example>新規メモ</Example>
      <ListenFor>新規メモ[を]</ListenFor>
      <ListenFor>新しいメモ</ListenFor>
      <Feedback>メモを言ってください</Feedback>
      <Navigate/>
    </Command>

    <Command Name="showOne">
      <Example>メモ1を表示</Example>
      <ListenFor>メモ{num}を表示[してください] </ListenFor>
      <Feedback>メモ{num}を表示します。 </Feedback>
      <Navigate Target="/ViewMemos.xaml"/>
    </Command>

    <PhraseList Label="num">
      <Item> 1 </Item>
      <Item> 2 </Item>
      <Item> 3 </Item>
    </PhraseList>
</CommandSet>
</VoiceCommands>

Ниже изложены правила проектирования VCD-файла.

  1. Префикс команды должен быть фонетически отличен от ключевых слов в Windows Phone. Это поможет избежать путаницы между вашим приложением и встроенной функцией смартфона. В случае английского языка (США) ключевыми словами являются call, dial, start, open, find, search, text, note и help.
  2. Сделайте префикс вашей команды подмножеством или естественной фонетической транскрипцией названия вашего приложения, а не чем-то совершенно другим. Это избавит пользователя от путаницы и уменьшит вероятность неправильного восприятия вашего приложения за другое приложение или функцию.
  3. Учтите, что распознавание требует точного совпадения в суффиксе команды. Таким образом, будет хорошей идеей придерживаться простого и легкого в запоминании префикса команды.
  4. Присваивайте каждому набору команд атрибут Name, чтобы вы могли обращаться к нему в своем коде.
  5. Храните элементы ListenFor в других элементах Command, фонетически отличных друг от друга, для уменьшения вероятности неправильного распознавания.
  6. Убедитесь, что элементы ListenFor в одной и той же команде являются разными способами указания этой команды. Если элементы ListenFor в команде соответствуют более чем одной операции, разносите их по разным командам. Это упростит обработку команд в вашем приложении.
  7. Не забывайте об ограничениях: 100 элементов Command в наборе команд, 10 элементов ListenFor в команде, всего 50 PhraseLists и всего 2000 элементов PhraseList во всех PhraseLists.
  8. Учитывайте, что распознавание элементов PhraseList требует полного совпадения, а не совпадения подмножества. Таким образом, чтобы распознавать и «Star Wars», и «Star Wars Episode One», вы должны включить обе фразы как элементы PhraseList.

В моем примере два элемента CommandSet, каждый со своими атрибутами xml:lang и Name. На каждое значение xml:lang может быть только один CommandSet. Атрибуты Name также должны быть уникальными, но ограничиваются лишь спецификацией значений этого атрибута. Хотя это не обязательно, настоятельно рекомендуется включать атрибут Name, так как он понадобится для доступа к CommandSet из кода приложения, реализующего этап 4. Кроме того, заметьте, что единовременно для вашего приложения активен только один CommandSet, а именно тот, чей атрибут xml:lang точно совпадает с таковым для текущего глобального механизма распознавания речи (global speech recognizer), заданного пользователем в SETTINGS/speech. Вы должны включать свои CommandSet для любых языков, которые требуются вашим пользователям.

Следующее, на что следует обратить внимание: элемент CommandPrefix. Рассматривайте его как псевдоним, который может быть произнесен пользователем для запуска вашего приложения. Это полезно, если в названии вашего приложения есть нестандартно произносимые или вообще непроизносимые (пропускаемые) буквы, например «Mag1c» или «gr00ve». Помните, что это слово или фраза должна быть чем-то, что механизм распознавания речи сможет опознать, и, кроме того, оно должно фонетически отличаться от встроенных в Windows Phone ключевых слов.

Заметьте, что элементы Example являются дочерними как для элемента CommandSet, так и для элемента Command. Элемент Example под CommandSet — это универсальный пример для вашего приложения, который появится в системной подсказке на экране «What can I say?» («Что я могу произнести?»), показанном на рис. 2. Однако элемент Example под Command специфичен для этой команды. Этот Example появляется на странице системной подсказки (рис. 3), отображаемой, когда пользователь касается названия приложения, показываемого на справочной странице на рис. 2.

*
Рис. 2. Справочная страница, показывающая примеры голосовых команд для установленных приложений

*
Рис. 3. Страница примеров голосовых команд для приложения Magic Memo

Кстати говоря, каждый дочерний элемент Command в CommandSet соответствует некоему действию, выполняемому приложением после запуска. В Command может быть несколько элементов ListenFor, но все они должны быть разными способами сообщить приложению выполнить действие (команду), для которого они являются дочерними.

Также заметьте, что в тексте элемента ListenFor есть две специальные конструкции. Квадратные скобки вокруг текста означают, что этот текст не обязателен, т. е. фрагмент речи пользователя может быть распознан как с этим текстом, так и без него. В фигурных скобках содержится метка, которая ссылается на элемент PhraseList. В примере с английским языком (США) на рис. 1 первый ListenFor под командой showOne имеет метку {num}, ссылающуюся на список фраз ниже. Вы можете рассматривать это как слот, который можно заполнить любой из фраз в списке, в данном случае цифр.

Что происходит, когда во фрагменте речи пользователя распознается некая команда? Глобальный механизм распознавания речи смартфона запустит приложение на странице, указанной в атрибуте Target элемента Navigate, который расположен под соответствующим Command, как будет показано позже, на этапе 3. Но сначала мы обсудим этап 2.

Включение поддержки голосовых команд

После включения VCD-файла в установочный пакет второй этап заключается в регистрации этого файла, чтобы Windows Phone 8 могла внести команды приложения в свою грамматическую систему. Для этого вызывается статический метод InstallCommandSetsFromFileAsync в классе VoiceCommandService, как показано на рис. 4. В большинстве приложений этот вызов осуществляется при первом запуске, но, разумеется, он может быть инициирован в любой момент. Реализация VoiceCommandService достаточно «интеллектуальна» и не станет ничего делать при последующих вызовах, если в VCD-файле не было никаких изменений. Так что можете не волноваться, когда метод вызывается при каждом запуске приложения.

Рис. 4. Инициализация VCD-файла из приложения

using Windows.Phone.Speech.VoiceCommands;
// ...
// Стандартный стереотипный метод класса App в App.xaml.cs
private async void Application_Launching(object sender,
  LaunchingEventArgs e)
{
  try // нужен для обнаружения ошибок компиляции VCD-файла
  {
    await VoiceCommandService.InstallCommandSetsFromFileAsync(
      new Uri("ms-appx:///MagicMemoVCD.xml"));
  }
  catch (Exception ex)
  {
    // Обработка исключения
  }
}

Как подразумевает название метода InstallCommandSetsFromFileAsync, операционной единицей является элемент CommandSet в VCD-файле, а не сам файл. Вызов этого метода проверяет все наборы команд, содержащиеся в файле, но устанавливает только те из них, атрибут xml:lang которых точно совпадает с таковым для глобального механизма распознавания речи. Если пользователь переключает глобальных распознаваемый язык на один из совпадающих с xml:lang другого CommandSet в вашем VCD, то загружается и активируется именно этот CommandSet.

Обработка голосовых команд

Теперь обсудим этап 3. Когда глобальный механизм распознавания опознает префикс команды и саму команду из вашего приложения, он запускает это приложение на странице, указанной в атрибуте Target элемента Navigate, или использует мишень задачи по умолчанию (default task target) (обычно MainPage.xaml для приложений Silverlight), если Target не задан. Он также дописывает к строке запроса пары «ключ-значение» для имени Command и значений PhraseList. Например, если распознанной фразой является «Magic Memo show memo number three», строка запроса может выглядеть, как показано ниже (реальная строка может варьироваться в зависимости от реализации или версии):

"/ViewMemos.xaml?voiceCommandName=show&num=3&
reco=show%20memo%20number%20three"

К счастью, вам не нужно разбирать строку запроса и самостоятельно выкапывать из нее параметры, так как они доступны в наборе QueryString объекта NavigationContext. Приложение может использовать эти данные, чтобы определить, было ли оно запущено голосовой командой, и, если да, соответственно обработать ее (скажем, в обработчике события Loaded страницы). На рис. 5 показан пример из приложения Magic Memo для страницы ViewMemos.xaml.

Когда глобальный механизм распознавания опознает префикс команды и саму команду из вашего приложения, он запускает это приложение на странице, указанной в атрибуте Target элемента Navigate.

Рис. 5. Обработка голосовых команд в приложении

// Выполняет соответствующее действие,
// если приложение было запущено голосовой командой
private void ViewMemosPage_Loaded(object sender, RoutedEventArgs e)
{
   // Остальной код опущен
  // Обрабатываем случай, когда страница
  // была запущена голосовой командой
  if (this.NavigationContext.QueryString != null
    && this.NavigationContext.QueryString.ContainsKey("voiceCommandName"))
  {
    // Страница запущена голосовой командой
    string commandName =
      NavigationContext.QueryString["voiceCommandName"];
    string spokenNumber = "";
    if (commandName == "showOne" &&
      this.NavigationContext.QueryString.TryGetValue("num",
          out spokenNumber))
    {
      // Команда была "Show memo number 'num'"
      int index = -1;
      if (int.TryParse(spokenNumber, out index) &&
        index <= memoList.Count && index > 0)
      { // отображаем указанную заметку
        this.Dispatcher.BeginInvoke(delegate
          { MessageBox.Show(String.Format(
          "Memo {0}: \"{1}\"", index, memoList[index-1])); });
      }
    }
    // Примечание: необходимости в блоке else нет, так как,
    // если запуск произошел другой VoiceCommand, тогда
    // commandName="showAll" и показывается страница
  }
}

Поскольку перейти на любую страницу можно не одним способом, код на рис. 5 сначала проверяет наличие ключа voiceCommandName в строке запроса, чтобы определить, запустил ли пользователь приложение голосовой командой. Если да, код проверяет имя команды и получает значение параметра num в PhraseList, который является номером заметки, нужной пользователю. В этой странице поддерживаются только две голосовые команды, и обработка проста, но в странице, которая может быть запущена многими голосовыми командами, пришлось бы использовать нечто вроде блока switch в commandName, чтобы решать, какое действие следует выполнить.

PhraseList в этом примере тоже прост; это лишь серия номеров — по одному на каждую сохраненную заметку. Однако можно представить более сложные сценарии, требующие списков фраз, заполняемых динамически (например, на основе данных с веб-сайта). Не обязательный этап 4, упомянутый ранее, как раз и позволяет реализовать списки PhraseList для таких сценариев. Обсудим это в следующем разделе.

Обновление списков фраз из вашего приложения

Вероятно, вы заметили проблему с VCD-файлом на рис. 1: значение num в PhraseList, определенное статически в VCD, поддерживает распознавание до трех элементов, но на самом деле в изолированном хранилище приложения будет храниться куда больше трех заметок. Для таких случаев, где список фраз со временем меняется, есть способ динамически обновлять список фраз из приложения, как показано на рис. 6. Это особенно удобно в приложениях, которым нужно распознавать фразы с использованием динамических списков, например загруженных фильмов, любимых ресторанчиков или достопримечательностей в районе текущего местонахождения смартфона.

Рис. 6. Динамическое обновление установленных списков фраз

// Обновление num в PhraseList, чтобы количество записей было
// равно количеству сохраненных заметок; здесь поддерживается
// "Magic Memo show memo 5", если сохранено 5 или более заметок
private async void UpdateNumberPhraseList(string phraseList,
  int newLimit, string commandSetName)
{
  // Вспомогательная функция, которая присваивает
  // строковому массиву {"1", "2" и т. д.}
  List<string> positiveIntegers =
    Utilities.GetStringListOfPositiveIntegers(Math.Max(1, newLimit));
  try
  {
    VoiceCommandSet vcs = null;
    if (VoiceCommandService.InstalledCommandSets.TryGetValue(
      commandSetName, out vcs))
    {
      // Обновляем num в списке фраз до новых значений
      await vcs.UpdatePhraseListAsync(phraseList, positiveIntegers);
    }
  }
  catch (Exception ex)
  {
    this.Dispatcher.BeginInvoke(delegate
      { MessageBox.Show("Exception in UpdateNumberPhraseList "
        + ex.Message); }
    );
  }
}

Хотя приложение Magic Memo этого не демонстрирует, динамически обновляемые списки фраз — отличный кандидат на обновление в пользовательском агенте, так как процедура обновления может выполняться «за кулисами», даже когда приложение не выполняется.

Ну вот мы и обсудили четыре этапа в реализации поддержки голосовых команд в приложении. Поэкспериментируйте сами с приложением-примером Magic Memo. Помните, что для загрузки VCD-файла приложение обычно нужно запустить один раз, но после этого вы можете произносить следующие команды:

В следующий раз: диалог внутри приложения

Реализация голосовых команд, рассмотренная в этой статье, является не более чем первым шагом к такой поддержке взаимодействия пользователей с вашим приложением в Windows Phone 8, которая имеется во встроенных приложениях вроде Text, Find и Call.

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


Ссылка: http://www.oszone.net/20059/Windows-Phone-8