C#での音声認識は、手軽く、精度もそこそこに、既存のマイクで行うことが出来る(霊的な引用を、あなたの心に届けよう)。

  • 38
    Like
  • 0
    Comment
More than 1 year has passed since last update.

はじめに

JuliusGoogle Cloud Speech APIなどの様々な音声認識エンジン・APIがありますが、C#で簡単に音声認識を利用したい。

じゃあどうするか

MS謹製の System.Speech もしくは Microsoft.Speech を利用すれば簡単に出来ます。
どちらも認識させたい語彙を登録すれば、結構いい感じに認識してくれます。

System.Speech Vs Microsoft.Speech

System.Speech Microsoft.Speech
用途 デスクトップ向け サーバー向け
トレーニング* 場合によっては必要 不要
事前準備 OS 組み込み(Windows Vista 以上) 別途インストールが必要
追加の言語パック なし(OS の標準言語のみ) あり(MSSpeech_SR_ja-JP_TELE.msiなど)
ディクテーション** 出来る 出来ない(関数は存在するがエラーを吐く)
パッケージ化 再頒布不可 アプリと共にパッケージ化可能

*コントロール パネル/コンピューターの簡単操作/音声認識 からトレーニングする?

**ディクテーションとは

特定の単語・文法に依らず、発話された自由文を逐次認識する方式である。
このため、汎用性は高いが、逆に発話された情報全てが認識対象となるため、ルール型のシステムと比較して高いコンピュータの処理能力を要求すること、認識精度が上げにくいため、発話者の特徴をあらかじめ登録するなど、何らかの工夫が必要であることなどがデメリットと考えられる。

音声認識・音声出力を利用した電子カルテ向け入出力アプリケーションの試作

事前準備

必要であれば以下の2つをインストールします。

実際に使う

System.Speech と Microsoft.Speech の両方に対応させているので、条件付きコンパイルシンボルなどで利用したい方を指定してください。
双方とも参照設定は必須であり、Microsoft.Speech では Microsoft.Speech.dll がローカルコピーされます。
なお、音声入力に使用するマイクは強制的に既定のデバイスとなります。デバイスを指定する方法あるんですかね?

ラッパークラス

SpeechRecognition.cs
#define Use_MicrosoftSpeech

#if Use_MicrosoftSpeech || Use_SystemSpeech
#if Use_MicrosoftSpeech
using Microsoft.Speech.Recognition;
#elif Use_SystemSpeech
using System.Speech.Recognition;
#endif

namespace MMFrame.Media
{
    /// <summary>
    /// 音声認識に関するクラス
    /// </summary>
    public static class SpeechRecognition
    {
        /// <summary>
        /// 音声認識エンジンを設定、取得します。
        /// </summary>
        public static SpeechRecognitionEngine Engine;

        /// <summary>
        /// 音声認識エンジンが利用可能かどうかを取得します。
        /// </summary>
        public static bool IsAvailable
        {
            get
            {
                return (Engine != null && !IsDestroyed);
            }
        }

        /// <summary>
        /// 音声認識を実行中かどうかを取得、設定します。
        /// </summary>
        public static bool IsRecognizing
        {
            get
            {
                return (IsAvailable && Engine.AudioState != AudioState.Stopped);
            }
        }

        /// <summary>
        /// このコンピュータでサポートしている音声認識エンジンを取得します。
        /// </summary>
        public static System.Collections.ObjectModel.ReadOnlyCollection<RecognizerInfo> InstalledRecognizers
        {
            get
            {
                return SpeechRecognitionEngine.InstalledRecognizers();
            }
        }

        /// <summary>
        /// 一時的に音声の一部を認識した場合のイベントを設定、取得します。
        /// </summary>
        public static System.Action<SpeechHypothesizedEventArgs> SpeechHypothesizedEvent;

        /// <summary>
        /// 信頼性の高い 1 つ以上の句を認識した場合のイベントを設定、取得します。
        /// </summary>
        public static System.Action<SpeechRecognizedEventArgs> SpeechRecognizedEvent;

        /// <summary>
        /// 信頼性の低い候補句のみ認識した場合のイベントを設定、取得します。
        /// </summary>
        public static System.Action<SpeechRecognitionRejectedEventArgs> SpeechRecognitionRejectedEvent;

        /// <summary>
        /// 音声認識が終了した場合のイベントを設定、取得します。
        /// </summary>
        public static System.Action<RecognizeCompletedEventArgs> SpeechRecognizeCompletedEvent;

        /// <summary>
        /// 音声認識エンジンが破棄されているかどうかを取得、設定します。
        /// </summary>
        private static bool IsDestroyed;

        /// <summary>
        /// コンストラクタ
        /// </summary>
        static SpeechRecognition()
        {
            IsDestroyed = true;
        }

        /// <summary>
        /// システム既定の音声認識エンジンを作成します。
        /// System.Speech が利用できる言語は OS の標準言語のみとなります。
        /// </summary>
        public static void CreateEngine()
        {
            CreateEngine<object>(null);
        }

        /// <summary>
        /// 認識する言語を指定して、音声認識エンジンを作成します。
        /// </summary>
        /// <param name="culture">認識する言語</param>
        public static void CreateEngine(System.Globalization.CultureInfo culture)
        {
            CreateEngine<System.Globalization.CultureInfo>(culture);
        }

        /// <summary>
        /// 音声認識エンジンの名前を指定して、音声認識エンジンを作成します。
        /// SR_MS_ja-JP_TELE_11.0 などの名前を指定します。
        /// </summary>
        /// <param name="engineName">音声認識エンジン名</param>
        public static void CreateEngine(string engineName)
        {
            CreateEngine<string>(engineName);
        }

        /// <summary>
        /// 音声認識エンジンを破棄します。
        /// </summary>
        public static void DestroyEngine()
        {
            if (!IsAvailable)
            {
                return;
            }

            Engine.SpeechHypothesized -= SpeechHypothesized;
            Engine.SpeechRecognized -= SpeechRecognized;
            Engine.SpeechRecognitionRejected -= SpeechRecognitionRejected;
            Engine.RecognizeCompleted -= SpeechRecognizeCompleted;
            Engine.UnloadAllGrammars();
            Engine.Dispose();

            IsDestroyed = true;
        }

        /// <summary>
        /// ルール型による音声認識方法を追加します。
        /// </summary>
        /// <param name="grammarName">文法名</param>
        /// <param name="words">追加する語彙</param>
        public static void AddGrammar(string grammarName, params string[] words)
        {
            Choices choices = new Choices();
            choices.Add(words);

            GrammarBuilder grammarBuilder = new GrammarBuilder();
            grammarBuilder.Append(choices);

            AddGrammar(grammarName, grammarBuilder);
        }

        /// <summary>
        /// ルール型による音声認識方法を追加します。
        /// </summary>
        /// <param name="grammarName">文法名</param>
        /// <param name="grammarBuilder">音声認識の文法</param>
        public static void AddGrammar(string grammarName, GrammarBuilder grammarBuilder)
        {
            Grammar grammar = new Grammar(grammarBuilder)
            {
                Name = grammarName
            };

            AddGrammar(grammar);
        }

        /// <summary>
        /// ルール型による音声認識方法を追加します。
        /// </summary>
        /// <param name="grammar">音声認識の文法</param>
        public static void AddGrammar(Grammar grammar)
        {
            if (!IsAvailable)
            {
                return;
            }

            Engine.LoadGrammar(grammar);
        }

        /// <summary>
        /// 自由発話のディクテーション型による音声認識方法を追加します。Grammar.Name は Dictation です。
        /// System.Speech.dll のみ使用可能です。
        /// </summary>
        public static void AddDictation()
        {
#if Use_SystemSpeech
            DictationGrammar dictation = new DictationGrammar()
            {
                Name = "Dictation"
            };

            AddGrammar(dictation);
#endif
        }

        /// <summary>
        /// 登録されている音声認識方法を削除します。
        /// </summary>
        /// <param name="grammarName">文法名</param>
        public static void ClearGrammar(string grammarName)
        {
            if (!IsAvailable)
            {
                return;
            }

            foreach (Grammar g in Engine.Grammars)
            {
                if (g.Name == grammarName)
                {
                    Engine.UnloadGrammar(g);
                    break;
                }
            }
        }

        /// <summary>
        /// 登録されているすべての音声認識方法を削除します。
        /// </summary>
        public static void ClearGrammar()
        {
            if (!IsAvailable)
            {
                return;
            }

            Engine.UnloadAllGrammars();
        }

        /// <summary>
        /// 非同期で音声認識を開始します。
        /// </summary>
        /// <param name="multiple">常に音声を認識する場合は true</param>
        public static void RecognizeAsync(bool multiple)
        {
            if (IsRecognizing || Engine.Grammars.Count <= 0)
            {
                return;
            }

            RecognizeMode mode = (multiple) ? RecognizeMode.Multiple : RecognizeMode.Single;
            Engine.RecognizeAsync(mode);
        }

        /// <summary>
        /// 現在の音声認識操作の完了を待たずに非同期認識を終了します。
        /// </summary>
        public static void RecognizeAsyncCancel()
        {
            if (!IsRecognizing)
            {
                return;
            }

            Engine.RecognizeAsyncCancel();
        }

        /// <summary>
        /// 現在の音声認識操作の完了後に非同期認識を終了します。
        /// </summary>
        public static void RecognizeAsyncStop()
        {
            if (!IsRecognizing)
            {
                return;
            }

            Engine.RecognizeAsyncStop();
        }

        // 音声認識エンジンを作成します。
        private static void CreateEngine<T>(object arg)
        {
            if (IsAvailable)
            {
                return;
            }

            if (arg == null)
            {
                Engine = new SpeechRecognitionEngine();
            }
            else if (typeof(T) == typeof(System.String))
            {
                Engine = new SpeechRecognitionEngine((string)arg);
            }
            else if (typeof(T) == typeof(System.Globalization.CultureInfo))
            {
                Engine = new SpeechRecognitionEngine((System.Globalization.CultureInfo)arg);
            }
            else
            {
                return;
            }

            IsDestroyed = false;

            Engine.SetInputToDefaultAudioDevice();

            Engine.SpeechHypothesized += SpeechHypothesized;
            Engine.SpeechRecognized += SpeechRecognized;
            Engine.SpeechRecognitionRejected += SpeechRecognitionRejected;
            Engine.RecognizeCompleted += SpeechRecognizeCompleted;
        }

        // 一時的に音声の一部を認識した場合のイベント
        private static void SpeechHypothesized(object sender, SpeechHypothesizedEventArgs e)
        {
            if (e.Result != null && SpeechHypothesizedEvent != null)
            {
                SpeechHypothesizedEvent(e);
            }
        }

        // 信頼性の高い 1 つ以上の句を認識した場合のイベント
        private static void SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
        {
            if (e.Result != null && SpeechRecognizedEvent != null)
            {
                SpeechRecognizedEvent(e);
            }
        }

        // 信頼性の低い候補句のみ認識した場合のイベント
        private static void SpeechRecognitionRejected(object sender, SpeechRecognitionRejectedEventArgs e)
        {
            if (e.Result != null && SpeechRecognitionRejectedEvent != null)
            {
                SpeechRecognitionRejectedEvent(e);
            }
        }

        // 音声認識が終了した場合のイベント
        private static void SpeechRecognizeCompleted(object sender, RecognizeCompletedEventArgs e)
        {
            if (e.Result != null && SpeechRecognizeCompletedEvent != null)
            {
                SpeechRecognizeCompletedEvent(e);
            }
        }
    }
}
#endif

そして利用する方です。
こちらも参照設定が必要です。

利用側

Form1.cs
using Microsoft.Speech.Recognition;

namespace TestClass
{
    public partial class Form1 : System.Windows.Forms.Form
    {
        public Form1()
        {
            InitializeComponent();

            MMFrame.Media.SpeechRecognition.CreateEngine("SR_MS_ja-JP_TELE_11.0");

            foreach (RecognizerInfo ri in MMFrame.Media.SpeechRecognition.InstalledRecognizers)
            {
                logTextBox1.Write(ri.Name + "(" + ri.Culture + ")");
            }

            MMFrame.Media.SpeechRecognition.SpeechRecognitionRejectedEvent = (e) =>
            {
                logTextBox1.Write("認識できません。");
            };

            MMFrame.Media.SpeechRecognition.SpeechRecognizedEvent = (e) =>
            {
                logTextBox1.Write("確定:" + e.Result.Grammar.Name + " " + e.Result.Text + "(" + e.Result.Confidence + ")");
            };

            MMFrame.Media.SpeechRecognition.SpeechHypothesizedEvent = (e) =>
            {
                logTextBox1.Write("候補:" + e.Result.Grammar.Name + " " + e.Result.Text + "(" + e.Result.Confidence + ")");
            };

            MMFrame.Media.SpeechRecognition.SpeechRecognizeCompletedEvent = (e) =>
            {
                if (e.Cancelled)
                {
                    logTextBox1.Write("キャンセルされました。");
                }

                logTextBox1.Write("認識終了");
            };
        }

        private void AddGrammar()
        {
            MMFrame.Media.SpeechRecognition.AddGrammar("weather", new string[] { "今日もイイ天気" });

            string[] words = new string[] { "オロナイン", "ドロヘドロ", "焼きそば", "ニュートリノ" };
            MMFrame.Media.SpeechRecognition.AddGrammar("words", words);

            Choices choices1 = new Choices();
            choices1.Add(new string[] { "殲滅戦", "電撃戦", "打撃戦", "防衛戦", "包囲戦", "突破戦", "退却戦", "掃討戦", "撤退戦" });
            GrammarBuilder grammarBuilder1 = new GrammarBuilder();
            grammarBuilder1.Append(choices1);
            grammarBuilder1.Append("が好きだ");
            MMFrame.Media.SpeechRecognition.AddGrammar("seelowe", grammarBuilder1);

            Choices choices2 = new Choices();
            choices2.Add(new string[] { "平原", "街道", "塹壕", "草原", "凍土", "砂漠", "海上", "空中", "泥中", "湿原" });
            GrammarBuilder grammarBuilder2 = new GrammarBuilder();
            grammarBuilder2.AppendWildcard();
            grammarBuilder2.Append("は");
            grammarBuilder2.Append(new SemanticResultKey("field", new GrammarBuilder(choices2)));
            grammarBuilder2.Append("が好きです");
            MMFrame.Media.SpeechRecognition.AddGrammar("field", grammarBuilder2);
        }

        private void button1_Click(object sender, System.EventArgs e)
        {
            MMFrame.Media.SpeechRecognition.RecognizeAsync(true);
        }

        private void button2_Click(object sender, System.EventArgs e)
        {
            MMFrame.Media.SpeechRecognition.RecognizeAsyncCancel();
        }

        private void button3_Click(object sender, System.EventArgs e)
        {
            MMFrame.Media.SpeechRecognition.RecognizeAsyncStop();
        }

        private void button4_Click(object sender, System.EventArgs e)
        {
            MMFrame.Media.SpeechRecognition.CreateEngine("SR_MS_ja-JP_TELE_11.0");
        }

        private void button5_Click(object sender, System.EventArgs e)
        {
            MMFrame.Media.SpeechRecognition.DestroyEngine();
        }

        private void button6_Click(object sender, System.EventArgs e)
        {
            AddGrammar();
        }

        private void button7_Click(object sender, System.EventArgs e)
        {
            MMFrame.Media.SpeechRecognition.ClearGrammar();
        }
    }
}

使ってみるとこんな感じ

実際に認識させている画面です。こんな感じになります。
今回の例では長めの文章はありませんが、ある程度であれば問題なく認識します。
img.png

おわりに

実際に使ってみると意外と認識して驚きます。
PCの近くにマイクを置いて使うのであれば、十分実用性があると思います。