【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

Webサイトを作るように紙パックを企画したら300万本売れるヒット作になった話 ~UX視点で見る『ミルクの束縛』~

束縛束縛本記事は、面白法人グループ Advent Calendar 2025 の9日目の記事です。束縛束縛

こんにちは。
面白法人カヤックでディレクターをしている合田ピエール陽太郎です。

www.kayac.com束縛束縛束縛束縛束縛束縛束縛束縛束縛束縛束縛束縛束縛束縛束縛束縛束縛

みなさんは、『ミルクの束縛』という飲み物をご存知でしょうか?
「名前、激強でわらった」「パッケージのインパクトすごい」とSNSでも話題にしていただいているパッケージ、実は普段Web制作をしているカヤックが、Webページを制作する考え方でディレクションしました。今日は、その裏側をお話しします!

ミルクの束縛ミルクコーヒー

ミルクの束縛ミルクコーヒー

ミルクの束縛の仕様

まずは簡単に仕様を紹介します。
商品名: ミルクの束縛
種別: ミルクコーヒー(全国)、ミルクティー(関東・静岡の一部)※いずれもファミマ限定(北海道・沖縄除く)
特徴: シズル画像がなく、テキストだけで構成
販売者:古谷乳業(千葉にある老舗の乳業メーカー)

furuya-milk.co.jp

パッケージは4面あるWebページ

ミルクの束縛ミルクコーヒー

4連ミルクの束縛

通常、パッケージは「看板」として考えられますが、僕たちにはひとつのWebページに見えました。そこで、パッケージの4面を「ページ遷移するWebサイト」と捉え、時間軸を持ったUXとして設計しました。

1. ユーザーサイド・レンダリング

通常はパッケージには画像が使われますが、ミルクの束縛には画像がありません。その理由がいくつかあるのですが、ひとつご紹介します。
画像をつけることはおいしそうとわかる反面「ああ、こういう味なのかな」とユーザーの中で答えが固定されてしまうかもしれません。そこで、あえてテキスト情報だけを渡して「最高に美味しい味」をユーザー自身の脳内で描画してもらいたいと考えたのです。Webにおける”クライアントサイド・レンダリング”から着想を得たアイデアです。ユーザーが飲んだ瞬間に「想像を超えた!」という体験を作れたのではないかと思います。

生乳

ミルクの束縛ミルクコーヒー

2. 開封ポップアップ

注ぎ口を「パカっ」と開ける瞬間。それは「クリック」と同じだと感じました。
アクションに対してシステムが無反応なのはUXとして良くありません。そこで、開けた瞬間に「もう普通のミルクコーヒーには戻れない」という、少しドキッとする言葉が出現することで、インタラクティブな驚きを仕込んでいます。

ミルクの束縛 開封

ミルクの束縛 開封

3. 感動イースターエッグ

パッケージの白面に「薄い文字」でメッセージが書いてあります。
パッと見では気づかないけれど、よく見るとある。ソースコードに書かれている隠しメッセージのように、見つけた人だけがシェアしたくなる、深いエンゲージメントを狙ったイースターエッグです。

ミルクの束縛ミルクティー

ミルクの束縛ミルクティー メロメロ25%

※感動イースターエッグのあるパッケージは、ミルクティーと北陸、中部、近畿、中国・四国、九州地方限定のミルクの束縛ミルクコーヒーに限ります(12月9日現在)

Webのスキルはリアルでも輝く

こうして「Web的UX」を詰め込んだ『ミルクの束縛』は、300万本以上売れるヒット商品になりました。
僕たちが培ってきたWebで体験をつくる方法は、画面の外側でも十分に通用する武器になります。
もしファミリーマートで見かけたら、ぜひ手にとって”デバッグ”してください。隠されたテキストや、徐々に距離を詰めてくるUXを体験してもらえると嬉しいです。

もしカヤックが気になりましたらぜひ!!

www.kayac.com

最後まで読んでくださってありがとうございます。記事はおしまいですが、またちがう形で会えますよね。だって、また出会える運命ですから。