.NET 標準で音声認識を試す（System.Speech.Recognition）

# はじめに

*こんにちは、のんびりエンジニアのたっつーです。*
*[ブログ](https://www.fast-system.jp/)を運営しているのでよろしければ見てください。*

対象物呼び出しと、それに対しての指示を同時に認識するようなサンプルを書いてみました。
非常に簡単に実装できるのでぜひ試してみてください。

# 実行結果

- PCにマイクを接続する
- 「田中さん」「佐藤さん」「佐々木さん」と声をかけると、「☆呼ばれました」が表示されます
- 「佐藤さん静かにして」と声をかけると「☆静かにしよう！」が表示されます

![image.png](https://qiita-image-store.s3.amazonaws.com/0/227880/e93fbd26-b2f4-5906-a42f-b6e7ff05c433.png)


# 使い方

- プロジェクトの参照設定で、```アセンブリ``` から ```System.Speech``` を追加してください。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/227880/128b54bb-42b4-5480-b412-d3594d3092f9.png)

- Program.cs

VoiceCommand をインスタンス化して Open() メソッドを呼び出します。

```C#:Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace test
{
    public class Program
    {
        static void Main(string[] args)
        {
            using (var vc = new VoiceCommand())
            {
                vc.Open();
                System.Console.ReadKey();
            }
        }
    }
}
```

- VoiceCommand.cs

VoiceCommand.cs はリソースを使用するので IDispose を実装したり、Open/Closeメソッドを実装しています。

Choices を使用して登録した単語のみ認識しているようにしています（自由文法だと構文解析が必要になるため）。DictationGrammarなどを使用すると自由文法で音声認識が可能みたいです。


```C#:VoiceCommand.cs
using System;
using System.Linq;
using System.Speech.Recognition;

namespace test
{
    public class VoiceCommand : IDisposable
    {
        SpeechRecognitionEngine engine1;
        SpeechRecognitionEngine engine2;

        static string[] AwakeWord = new string[] { "佐藤さん", "田中さん", "佐々木さん" };
        static string[] SilentWords = new string[] { "静かにして", "黙って" };
        static string[] TalkWords = new string[] { "喋っていいよ", "声出していいよ" };

        public VoiceCommand()
        {
        }

        public void Open()
        {
            // 呼び出し
            Choices awake = new Choices(AwakeWord);
            this.engine1 = Open(new GrammarBuilder(awake));

            // 指示
            Choices actions = new Choices();
            actions.Add(SilentWords);
            actions.Add(TalkWords);
            GrammarBuilder gb = new GrammarBuilder();
            gb.Append(awake);
            gb.Append(actions);
            this.engine2 = Open(gb);
        }

        private SpeechRecognitionEngine Open(GrammarBuilder gb)
        {
            var engine = new SpeechRecognitionEngine();

            // 音声認識が認識処理を終えたときのイベントハンドラを設定する。
            engine.SpeechRecognized += recogEngine_SpeechRecognized;

            // 音声認識が推定処理を終えたときのイベントハンドラを設定する。
            engine.SpeechHypothesized += recogEngine_SpeechHypothesized;

            // SystemSpeech を利用したディクテーションを行う場合には、
            engine.LoadGrammar(new Grammar(gb));

            // 実行環境の標準の入力を音声認識エンジンの入力とする。
            engine.SetInputToDefaultAudioDevice();

            // 非同期の認識を継続して実行するようにして音声認識を開始する。
            engine.RecognizeAsync(RecognizeMode.Multiple);

            return engine;
        }

        public void Close()
        {
            if (this.engine2 != null)
            {
                this.engine2.SpeechRecognized -= recogEngine_SpeechRecognized;
                this.engine2.SpeechHypothesized -= recogEngine_SpeechHypothesized;
                this.engine2.Dispose();
                this.engine2 = null;
            }

            if (this.engine1 != null)
            {
                this.engine1.SpeechRecognized -= recogEngine_SpeechRecognized;
                this.engine1.SpeechHypothesized -= recogEngine_SpeechHypothesized;
                this.engine1.Dispose();
                this.engine1 = null;
            }
        }

        public void Dispose()
        {
            this.Close();
        }

        private void recogEngine_SpeechHypothesized(object sender, SpeechHypothesizedEventArgs e)
        {
            System.Console.WriteLine("○" + e.Result.Text + "(" + e.Result.Confidence + ")");
        }

        private void recogEngine_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
        {
            System.Console.WriteLine("●" + e.Result.Text + "(" + e.Result.Confidence + ")");

            // 処理を分ける
            bool isSilent = SilentWords.Any(x => e.Result.Text.Contains(x));
            bool isTalk = TalkWords.Any(x => e.Result.Text.Contains(x));
            if (isSilent)
                System.Console.WriteLine("☆静かにしよう！");
            else if (isTalk)
                System.Console.WriteLine("☆話してOK！");
            else
                System.Console.WriteLine("☆呼ばれましたよ");
        }
    }
}
```

# 終わりに

*よければ ブログ「[初心者向けUnity情報サイト](https://www.fast-system.jp/)」の方にも色々記載しているのでぜひご参照いただければと思います。*