次の方法で共有


Windows Phone

Windows Phone 8 アプリを音声対応にする (第 2 部: アプリ内での音声による対話)

F Avery Avery

コード サンプルのダウンロード

先月紹介したこの 2 部構成シリーズの第 1 部 (msdn.microsoft.com/magazine/jj721592) では、Windows Phone 8 アプリで音声コマンドを有効にする方法について説明しました。第 2 部では、実行中のアプリでユーザーが音声入出力を使用してアプリと対話する方法について説明します。

アプリが起動されたら、ユーザーとの携帯電話でのやり取りに音声入出力を使うことで、さまざまなシナリオを実現できます。その 1 つがアプリ内での音声による対話です。たとえば、ユーザーが Magic Memo アプリ (前回のコラムを参照) を起動してメイン ページに移動すると、音声認識を使用して新しいメモを入力したり、音声でフィードバックを受け取ったり、変更を確認したりできます。音声認識が問題なく機能すると、ユーザーは最初にスタート ボタンを長押しする以外は、携帯電話に触れずに複数のメモをすべて入力して保存できます。

音声による対話を利用してアプリ内で実現できるシナリオは、ほかにもたくさん想像できるでしょう。たとえば、ユーザーがメモ、動画、過去のデータなどを保存したお気に入りの一覧を表示するページに移動したら、音声認識を使用して項目を選択し、編集、再生、並べ替え、削除などの操作を実行できます。その後、音声出力によって選択内容を読み上げ、確認を求めるようにします。

ここからは、音声入出力を使用した例を簡単なものから複雑なものまで紹介し、シンプルな例を簡単に実装する方法や、高度なシナリオで使用できる豊富な機能の一部を取り上げます。

ユーザーとのコミュニケーション: 音声合成 API

コンピューターが生成する音声出力は、TTS や音声合成など、さまざまな呼び方があります (厳密に言えば、TTS は音声合成より広い範囲を意味します)。既に説明したように通知や確認に使うのが一般的ですが、書籍や画面の読み上げも重要な使用例のひとつです。

音声合成のシンプルな例: 最もシンプルな例として、たった 2 行のコードで、テキスト文字列を音声に変換します。Magic Memo サンプルから引用したコードを使った例を次に示します。

// Instantiate a speech synthesizer
private SpeechSynthesizer speechOutput = 
  new SpeechSynthesizer();
// ...
// Private method to get a new memo
private async void GetNewMemoByVoice()
{
  await speechOutput.SpeakTextAsync("Say your memo");
  // Other code for capturing a new memo
}

ユーザーがマイク ボタンをタップすると、使用しているオーディオ デバイスから「Say your memo」という音声が聞こえます。次は、ユーザー入力を音声認識を使って受け取るコードを追加して、この例を拡張します。

高度なシナリオ向けの TTS 機能: 音声出力を多用するアプリでは、音声の出力時に音量、ピッチ、発声速度の調節が必要になります。このような高度な使用方法に対応するには、新たに SpeakSsmlAsync メソッドと SpeakSsmlFromUriAsync メソッドの 2 つのメソッドを使用します。これらのメソッドは、音声やシンセサイザー エンジンのプロパティを音声出力するテキストに埋め込むため、入力が SSML (Speech Synthesis Markup Language) 形式で、World Wide Web コンソーシアム (W3C) XML 標準であることを想定しています。今回のコラムや Magic Memo コード ダウンロードに SSML 用のサンプル コードは用意していませんが、SSML の詳細については、MSDN ライブラリのリファレンス記事 (bit.ly/QwWLsu、英語) で確認できます (W3C の仕様については、bit.ly/V4DlgG (英語) を参照してください)。

シンセサイザー クラスには SpeakStarted イベントと BookmarkReached イベントもあります。また、各 Speak メソッドには、特定のイベントを生成する Speak メソッドのインスタンスを追跡するのに役立つ汎用の状態オブジェクトを 2 つ目のパラメーターとして受け取るオーバーロードがあります。SSML を使用し、イベントを処理することで、音声出力されているテキストを強調したり、段落の途中から Speak メソッドの呼び出しを再開したりする機能をコードから実行できます。

音声入力: 音声認識 API

アプリにおける音声認識の使用例を大まかに分けると、テキスト入力とコマンド コントロールの 2 つになります。1 つ目のテキスト入力では、アプリはユーザーの音声を単純にテキストとして受け取ります。これは、サンプル コードの "新しいメモ (new memo)" 機能など、ユーザーがほとんど何でも自由に発言できる場合に役立ちます。

2 つ目のコマンドとコントロールでは、ユーザーがボタンをタップしたり携帯電話の画面を指で操作するのではなく、音声でアプリを操作します。この使用例は特に、運転中や料理中など、両手を自由に使いたいシナリオで役立ちます。

音声認識のシンプルな例: アプリの音声認識の機能について詳しく説明する前に、数行のコードで実行できるテキスト入力の簡単な例を見てみましょう。

図 1 に示すように、認識オブジェクトを初期化し、認識セッションを開始して、結果を処理するためのコードを前述の GetNewMemoByVoice メソッドに追加します。

図 1 認識オブジェクトの初期化、認識セッションの開始、および結果の処理

private SpeechRecognizerUI speechInput = 
  new SpeechRecognizerUI();
// Set text to display to the user when recognizing
speechInput.Settings.ExampleText = 
  "Example: \"Buy roses\"";
speechInput.Settings.ListenText = "Say your memo";
// ...
// Private method to get a new memo
private async void GetNewMemoByVoice()
{
  await speechOutput.SpeakTextAsync("Say your memo"); // TTS prompt
  var recoResult =
    await speechInput.RecognizeWithUIAsync();
      // Uses default Dictation grammar
  Memo_TB.Text =
    recoResult.RecognitionResult.Text; // Do something with the result
}

もちろん、実際のコードはこれほど単純ではありません。Magic Memo サンプルを見てみると、try/catch ブロックや認識が成功したかどうかのチェックを確認できます。

サンプル アプリでマイク アイコンをタップしてこの例を試し、メモすることを話してみると、"認識中" であることを示す画面が表示され、確認の UI が表示された後、メモのテキスト ボックスに結果が挿入されます。バックグラウンドではさまざまな処理が実行されていますが、リモート サーバーで "文法" を使用して音声を認識しているものはありません。文法は基本的に、エンジンが認識する必要がある語い的なエントリ ("単語") とその順番を指定する一連の規則です。ここからは、音声認識 API の詳細と、認識文法を使用する方法について説明します。

音声認識 API の概要: 音声認識のコーディングについて詳しく説明する前に、API のクラスとその役割の概要を見てみましょう。図 2 は API の基本的なレイアウトを示しています。まず、2 つのボックスの名前に SpeechRecognizer が含まれています。

Speech Recognition API Design Overview
図 2 音声認識 API の設計の概要

アプリの音声認識で UI を表示する必要がない場合、または独自のカスタム UI を表示する場合、図 2 の中央左に示されている SpeechRecognizer クラスのコピーのインスタンスを作成します。このオブジェクトがこの API 内の音声認識の基本的な処理単位と考えられます。アプリではこのオブジェクトに必要な文法を追加します。初期化したら、RecognizeAsync メソッドを呼び出して実際の認識を実行します。SpeechRecognizer オブジェクトが IAsyncOperation<SpeechRecognitionResult> を実装するため、Completed コールバック関数で状態および結果のオブジェクトを使用できます。したがって、他のマネージ音声 API のように、認識の完了または拒否が個別のイベントとして発生することはありません。

名前からわかるように、最上位の SpeechRecognizerUI クラスでは、携帯電話のグローバルな音声 UI と一貫性がある既定の GUI をフィードバック、あいまいさの回避、および確認のために使って、音声認識を実行します。グローバルな音声 UI との互換性を保持し、コードを簡略化するため、多くのアプリでは前述の UI を表示しないクラスではなくこのクラスを使用します。SpeechRecognizerUI オブジェクトのインスタンスを作成するときは、2 つの重要なオブジェクトを使用します。1 つは、ユーザーに表示する UI テキストを設定する Settings オブジェクト、もう 1 つはこの後説明する文法を指定できる SpeechRecognizer オブジェクトです。初期化したら、親の SpeechRecognizerUI オブジェクトの RecognizeWithUIAsync メソッドを呼び出して、認識セッションを開始します。子の SpeechRecognizer オブジェクトの RecognizeAsync メソッドを使用する場合、SpeechRecognizer オブジェクトがスタンドアロンで使用されている (UI なしで使用されている) と認識されます。ここからは SpeechRecognizer を UI があるオブジェクト、RecognizeAsync をUI がないオブジェクトを表すと考えてください。

音声認識を使用する手順: Windows Phone 8 アプリで音声認識を使用するには、次の 4 つの基本手順を実行します。

  1. 認識プロセスで使用する文法を作成します (事前に定義済みの文法を使用する場合は不要です)。
  2. 必要に応じてプロパティを設定し、文法を追加して、SpeechRecognizer オブジェクトを初期化します。
  3. SpeechRecognizer.RecognizeAsync メソッドまたは SpeechRecognizerUI.RecognizeWithUIAsync メソッドを呼び出して認識セッションを開始します。
  4. 認識結果を処理し、適切な操作を実行します。

図 1 は、1 つ目の文法を作成する手順以外のすべての手順を示しています。事前に定義する Dictation 文法が既定の文法になるため、文法を作成したり Grammars コレクションに追加したりする必要はありません。

これらの手順を実装するコードは、音声認識で使用する文法の種類によって大きく変わります。ここからは、Windows Phone 8 での音声認識文法の概要とその使用方法について説明します。

音声認識文法の概要

最近の音声認識エンジンはすべて文法を使用し、ユーザーの音声に一致する内容を見つけるためにエンジンが検索する必要があるフレーズのセット (これ以降は "検索領域" と呼びます) を限定します。これにより、音声認識の正確性を向上します。文法規則では、フレーズの認識を数字の一覧のように単純にしたり、よく使われる話し言葉のように複雑にしたりできます。

Windows Phone 8 の音声 API では、3 つの方法で文法を指定できます。どの方法でも、文法を SpeechRecognizer オブジェクトの文法コレクションに追加します。

単純な文法一覧: アプリ独自の文法を指定する最も簡単な方法は、認識エンジンが認識する必要があるすべてのフレーズの一覧を単純な文字列配列として指定する方法です。このような文法一覧は、デバイスの音声認識エンジンが処理します。文法一覧を作成および追加するコードは簡単で、認識するボタン名の静的リストを次のように指定します。

commandReco.Recognizer.Grammars.AddGrammarFromList(
  "mainPageCommands", new string[] { "cancel", "save", "quit" });

Magic Memo サンプルでは、もう少し高度なコードを実行します。ページ上のすべてのボタン コントロールの Content 属性を探し、Content text を文字列の一覧に追加することで、フレーズの一覧を作成しています。詳細については、MainPage.xaml.cs の InitCommandGrammar メソッドを参照してください。

文法一覧を使用して認識セッションの結果を処理するには、SpeechRecognitionUIResult (UI なしのバージョンを使用している場合は SpeechRecognitionResult) の Text プロパティを読み取ります。この処理は、たとえば、switch ステートメントを使用して実行できます (図 3 参照)。

図 3 認識セッションの結果の処理

switch (result.RecognitionResult.Text.ToLower())
{
  case "cancel":
  // Cancel code
    break;
  case "save":
  // Save memo code
    break;
  case "quit":
    break;
  default:
    break;
}

詳細な例については、MainPage.xaml.cs の CommandCompleted コールバックを参照してください。

定義済み文法: Windows Phone 8 の音声 API では、Dictation と WebSearch の 2 つの定義済み文法を使用できます。Dictation は Short Message Dictation とも呼ばれ、組み込みのメール作成アプリで使用されているものと同じ文法を使用します。これに対して、WebSearch はオンライン検索で使用されるフレーズに最適化されています。組み込みの検索コマンドでは同じ WebSearch 文法を使用します。

いずれの定義済み文法の検索領域も非常に広く、マイクロソフトの音声 Web サービスを使用するリモート音声認識を利用できるだけの処理能力が必要です。通常、音声認識が誤認識を行ったり、広範な結果を検出したりする可能性があるため、これらの文法はコマンドやコントロールにはあまり適していません。

定義済み文法の主なメリットは、アプリに簡単に実装できるという点にあります。たとえば、図 1 で既定の Dictation 文法ではなく WebSearch 文法を使用するには、RecognizeWithUIAsync メソッドの呼び出しの前に次の行を追加するだけです。

speechInput.Recognizer.Grammars.AddGrammarFromPredefinedType(
  "webSearch", SpeechPredefinedGrammar.WebSearch);

結果の Text プロパティにアクセスして、定義済み文法の認識結果を処理します (図 1 参照)。

音声認識文法仕様形式の文法: 音声認識文法仕様 (SRGS) は XML 形式の W3C 標準です。この形式と使用方法の詳細については、MSDN ライブラリの記事「SRGS 文法の XML 参照」(bit.ly/SYnAu5、英語)、W3C 仕様 (bit.ly/V4DNeS、英語)、またはオンラインで "SRGS 文法" を検索して確認できるさまざまなチュートリアル Web ページを参照してください。SRGS 文法を使用すると、文法の作成、テスト、およびデバッグに労力を費やすことになりますが、任意の項目を指定する機能や、項目、規則、ルール参照、特殊な規則、およびセマンティクスを繰り返す機能など、さまざまな機能を利用できます。Windows Phone 8 では、SRGS 文法は携帯電話のローカル認識エンジンでのみ使用されます。つまり、リモート サービスで使用されることはありません。

SRGS 文法を追加するには、次のようなコードを使用して、アプリのインストール パスに置いた文法ファイルの URI を参照します。

commandReco.Recognizer.Grammars.AddGrammarFromUri(
  "srgsCommands", new Uri("ms-appx:///ViewMemos.grxml"));

SRGS 文法の主なメリットの 1 つは、セマンティクス値を指定して、認識された音声 (通常は RecognitionResult.Text プロパティで利用できます) にアクセスすることなくさまざまなユーザー応答の処理を簡略化できることです。

SRGS セマンティクスは、<tag> 要素と ECMAScript のサブセットを使用して SRGS 文法の変数に割り当てるオブジェクトです (このオブジェクトは実際には多くの場合文字列です)。SRGS セマンティクスには、次のように認識されたテキストを直接使用するよりも優れたメリットが得られます。

  1. 処理の簡略化: 同じ意味に複数の形式が当てはめられるような場合に認識結果のテキストを解析しなくてもユーザーの目的を特定できます。たとえば、セマンティクスを使用すると、「はい」、「うん」、「賛成」、「OK」、「ええ」などの肯定的な意味の言葉をすべて 1 つのセマンティクス値「はい」に関連付けることができます。
  2. 容易なローカライズ: すべての言語に一貫性のあるセマンティクス値を使用すると、同じ分離コードを使用して、音声をサポートするすべての言語の言葉を処理できます。

これらの考え方を説明するため、Magic Memo サンプルでは ViewMemos.xaml ページを制御するための簡単な文法として ViewMemos.grxml を使用しています。セマンティック タグを含むこの文法ファイルからの引用を図 4 に示します。ViewMemos.xaml.cs の micImage_Tap 関数 (図 5 に引用) は、ユーザーの音声を操作に関連付けるセマンティクス値の使い方を示しています。

図 4 ViewMemos.grxml の SRGS 文法からの引用

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE grammar PUBLIC "-//W3C//DTD GRAMMAR 1.0//EN"
  "http://www.w3.org/TR/speech-grammar/grammar.dtd">
<!-- the default grammar language is US English -->
<grammar xmlns="http://www.w3.org/2001/06/grammar"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://www.w3.org/2001/06/grammar
           http://www.w3.org/TR/speech-grammar/grammar.xsd"
         xml:lang="en-US" version="1.0" tag-format="semantics/1.0"
           root="buttons">
  <rule id="buttons" scope="public">
    <one-of>
      <!--The 'process' semantic can be one of 'clear',
        'save', 'new', or 'select'-->
      <item>
        <!--Reference to the internal rule "scope" below-->
        Clear <ruleref uri="#scope" type="application/srgs+xml"/>
        <tag>out.process="clear";out.num = rules.latest();</tag>
      </item>
      <item>
        Save
        <item repeat="0-1">changes</item>
        <tag>out.process="save";</tag>
      </item>
      <item>
        Enter new
        <tag>out.process="new";</tag>
      </item>
      <item>       
        Select
        <item repeat="0-1">memo</item> <!-- Optional words -->
        <item repeat="0-1">number</item>
        <!--Reference to the internal rule "number" below -->
        <ruleref uri="#number" type="application/srgs+xml"/>
        <tag>out.process="select";out.num =
          rules.latest();</tag>
      </item>
    </one-of>
  </rule>
  <rule id="scope" scope="private">
    <one-of> <!-- Can be "all", "selected" or a number from the
      'number' rule -->
      <item>
        all <tag>out.scope="all";</tag>
      </item>
      <item>
        selected <tag>out.scope="selected";</tag>
      </item>      <item>
        <item repeat="0-1">memo</item> <!-- Optional words -->
        <item repeat="0-1">number</item>
        <ruleref uri="#number" type="application/srgs+xml"/>
      </item>
    </one-of>
  </rule>
 <rule id="number" scope="public">
      <item>
        1
      </item>
    <!-- See ViewMemos.grxml for the remainder
      of the items in this block -->
  </rule>
</grammar>

図 5 セマンティクス プロパティを使用した認識結果の処理

// micImage Tap handler, excerpted from ViewMemos.xaml.cs
private async void micImage_Tap(object sender, GestureEventArgs e)
{
  var commandResult = await commandReco.RecognizeWithUIAsync();
  if (commandResult.ResultStatus ==
    SpeechRecognitionUIStatus.Succeeded)
  {
    var commandSemantics = commandResult.RecognitionResult.Semantics;
    SemanticProperty process = null;
    if (commandSemantics.TryGetValue("process", out process))
    {
      // In general a semantic property can be any object,
      // but in this case it's a string
      switch (process.Value as string)
      {
        // For this grammar the "process" semantic more or less
        // corresponds to a button on the page
        case "select":
        // Command was "Select memo number 'num'"
          break;
        case "clear":
        // Command was "Clear memo number 'num,'" "Clear all"
        // or "Clear Selected"
          break;
        case "save":
        // Command was "Save" or "Save Changes"
          break;
        case "new":
        // Command was "Enter new"
          break;
        default:
          break;
      }
    }
  }
}

このサンプルでは、セマンティクスで実現できることの概要を示しているだけです。詳細については、まず、MSDN ライブラリの記事「タグ要素の使用」(bit.ly/PA80Wp、英語) を参照してください。セマンティクスの W3C 標準については、bit.ly/RyqJxc (英語) を参照してください。

Magic Memo サンプルで ViewMemos ページに移動し、マイク アイコンをタップして、この文法を実行できます。ViewMemos.xaml.cs ファイルには、認識結果で返されるセマンティクス値を表示およびデバッグするために (#define SemanticsDebug を使用して) アクティブ化できる #define セクションのコードなどの分離コードがあります。

同じ認識オブジェクトで複数の文法を使用する: ここで、SpeechRecognizer オブジェクトで複数の文法を使用できるかという疑問が生じるのは当然のことです。SpeechRecognizer オブジェクトでは複数の文法を使用できますが、一部制限があります。複数の文法を使用する場合のガイドラインおよびコーディング手法をいくつか次に示します。

  1. 定義済み文法を追加する場合、他の文法は追加できません。また、定義済み文法の有効期間中、認識オブジェクトに関連付けられているのがこの文法のみの場合、この定義済み文法を無効にすることはできません。
  2. 1 つの認識オブジェクトに複数のカスタム文法 (文法一覧と SRGS 文法) を追加でき、アプリのさまざまなシナリオに応じて文法の有効、無効を切り替えることができます。
    1. 特定の文法にアクセスするには、文法名 (AddGrammar メソッドの呼び出しに渡す文字列パラメーター) を Grammars コレクションのキーとして使用します。
    2. 特定の文法を有効または無効にするには、文法の Enabled ブール値を true または false に設定します。たとえば、次のコードでは "buttonNames" という名前の文法が無効になります。
           myRecognizer.Grammars["buttonNames"].Enabled = false;
  1. AddGrammar メソッドを呼び出すと、文法がキューに配置され、処理が完了するまで待機しますが、解析されたり読み込まれたりすることはありません。文法は、RecognizeAsync メソッドの最初の呼び出し、または PreLoadGrammarsAsync メソッドの任意の呼び出しでコンパイルされて読み込まれます。文法を実際に使用する前に後者のメソッドを呼び出すと、RecognizeAsync メソッドから結果を返すときに待機時間が短縮されるため、ほとんどのシナリオでの使用をお勧めします。

新たな革新的アプリ

Windows Phone 8 のアプリの音声機能はスマートフォンのすべてのサービスに対応しており、デバイス上のサービスおよびリモート認識サービスを音声で操作でき、完全に機能する初めての開発者プラットフォームです。音声コマンドおよびアプリ内での音声による対話を使用すると、ユーザーが楽しめる魅力的なシナリオにアプリを活用することができます。このような音声機能を利用すると、皆さんのアプリが話題になり、市場で新たな "革新的アプリ" となるでしょう。

F Avery Bishop は、20 年以上ソフトウェア開発に携わってきました。そのうち 12 年間はマイクロソフトに勤務し、音声認識プラットフォームのプログラム マネージャーを務めています。彼は、複雑なスクリプト サポート、多言語アプリ、音声認識などのトピックについて、アプリの自然言語サポートに多数の記事を公開しています。

この記事のレビューに協力してくれた技術スタッフの Eduardo Billo、Rob Chambers、Gabriel Ghizila、Michael Kim、および Brian Mouncer に心より感謝いたします。