【Grammar Recognizer】UnityEngine.Windows.Speechのキーワード認識で、複数単語を連続認識したい

この記事は面白法人グループAdvent Calendar 202510日目の記事です。

こんにちは。 カヤックボンドでエンジニアをやっております青木です!

今回は、Unity + Windows環境で利用できる音声認識 UnityEngine.Windows.Speech で行うキーワード認識について、基本的な使用方法と複数単語をテンポ良く認識させる方法を記事化してみました!


目次


はじめに

Unityで音声認識をする方法は複数あります。 Google Cloud Speech APIなど外部サービスを利用する方法や、OpenAI Whisperなどローカルの音声認識エンジンを利用する方法、Web Speech APIの認識結果をwebsocketで受け取る方法などもあるようです。

その中で、UnityEngine.Windows.Speechは、UnityでWindows標準の音声認識機能を利用できるAPIです。

Windowsに組み込まれているWindows Speech Recognitionを利用して認識を行うため、Win環境であればツールのインストールなども不要、無料で利用できます

3つの認識方式

UnityEngine.Windows.Speechには、以下3種類の認識方式が実装されています。

認識方式 説明
DictationRecognizer 検知した文言全てを認識する方式
KeywordRecognizer 予め指定したキーワードのみを認識する方式
GrammarRecognizer KeywordRecognizerの上位互換 SRGS文法で認識文法をより細かく指定可能

今回は、キーワード認識であるKeywordRecognizerGrammarRecognizerについて扱います。

最も簡単に使えるKeywordRecognizerとその欠点

KeywordRecognizerは最も簡単に使える認識方式で、キーワードをArrayで渡して起動するだけで利用できます。

using UnityEngine;
using UnityEngine.Windows.Speech;

private KeywordRecognizer _keywordRecognizer;
private readonly string[] _keywords = {
    "ニンニク",
    "ヤサイ",
    "アブラ",
    "スクナメ",
    "カラメ",
    "マシ"
};
private void Start()
{
    StartKeywordRecognizer();
}
private void StartKeywordRecognizer()
{
    _keywordRecognizer = new KeywordRecognizer(_keywords);
    _keywordRecognizer.OnPhraseRecognized += OnPhraseRecognized;
    _keywordRecognizer.Start();
}
private void OnPhraseRecognized(PhraseRecognizedEventArgs args)
{
    // 認識結果
    Debug.Log(args.text);
}

認識されるマイクは、システムデフォルトのマイクとなっています。

認識精度については非常に良く満足のいくものですが、実用には1点問題点を感じていました。

連続して複数の単語を発話した際、1つしか認識できない という問題です。 例えば、「アブラ、カラメ」と発話した場合、「アブラ」は無視され「カラメ」のみが認識されることとなります。
(5,6単語連続で発話したら、2単語程度認識された)

もしこの制約下で、複数単語を連続して発話したい場合は、

Aを発話 → 認識完了を待つ → Bを発話.....

待機を間に挟みつつ発話する必要が出てきてしまいます。
テンポが悪くて問題ですね。

個人的には、複数の修飾句を組み合わせて1つの文言を作るような使い方がしたかったので、このまま使用することはできませんでした。

docs.unity3d.com

よりテンポ良く認識可能なGrammarRecognizer

KeywordRecognizerの上位互換であるGrammarRecognizerを使用すれば、以上の問題を解決することができます。

GrammarRecognizaerはより複雑な文法を指定してキーワード認識ができるクラスで、 SRGS構文に基づき、ファイル指定で文法を設定できます。

using System.IO;
using UnityEngine;
using UnityEngine.Windows.Speech;

// 下記のGrammar.xmlを、Assets/StreamingAssets/SRGSフォルダに設置してください
private static readonly string GRAMMAR_FILE_PATH = Application.streamingAssetsPath + "/SRGS/Grammar.xml";
private GrammarRecognizer _grammarRecognizer;

private void Start()
{
    StartGrammarRecognizer();
}
private void StartGrammarRecognizer()
{
    if (!File.Exists(GRAMMAR_FILE_PATH))
    {
        return;
    }
    _grammarRecognizer = new GrammarRecognizer(GRAMMAR_FILE_PATH);
    _grammarRecognizer.OnPhraseRecognized += OnPhraseRecognized;
    _grammarRecognizer.Start();
}
private void OnPhraseRecognized(PhraseRecognizedEventArgs args)
{
    // 認識結果
    Debug.Log(args.text);
}
<grammar version="1.0" xml:lang="ja-JP" root="rootRule" xmlns="http://www.w3.org/2001/06/grammar">
  <rule id="rootRule">
    <item repeat="0-">
      <one-of>
        <item>ニンニク</item>
        <item>ヤサイ</item>
        <item>アブラ</item>
        <item>スクナメ</item>
        <item>カラメ</item>
        <item>マシ</item>
      </one-of>
    </item>
  </rule>
</grammar>

上記構文は、one-ofに囲まれた単語を何回でも繰り返し認識できるというもので、
単語間は繋げて発話しても問題なく認識が可能となっています。

上記の単語を一気に発話して認識させてみましたが、一単語も落ちず認識させることができました
複数単語を組み合わせる場合のスムーズさは格段に向上し、快適にボイスコマンドを体験できる状態になったと思います

なお、気になった点として、発話が完全に完了するまで認識を開始してくれないという問題がありました。
この構文では、単語のリピート回数に上限を設定していないため、発話が完全に完了するまで認識を開始してくれません。
単語数が多くなってくるとリードタイムが気になってきそうです。

認識回数に上限を設けても良い場合は
<item repeat="0-3">
など上限を指定することで、規定回数で打ち切って認識開始してくれるので、利用しても良いかもしれません。

docs.unity3d.com

まとめ

Windows + Unity環境で、音声認識を行う2つの方法について記事にしました。
前準備不要・無料で手軽に導入でき、キーワード認識であれば違和感なく認識され、さらに表記ゆれ・変換ミスなどへの対策も不要なため、簡単なデスクトップアプリでボイスコマンドを行いたい時などに導入してみてはいかがでしょうか。

では、最後まで見て頂いてありがとうございました!

kayac.bond