「BCCSkeltonでもやったTextToSpeech、DirectShowなどはどんな形でC#で使われているのだろう?」という疑問から昨年末から続いてきた【WPF】と【SAPI】シリーズですが、今回で一応お開きということになります。おかげさまで(知らずに使っていた)WinFormsとは異なるUIプラットフォームのWPF(従って、XamlやMSBuild、UIElementやContent等周辺知識も含めて)や現在のC#における音声合成・認識について「一舐め」させていただいた気分です。
さて音声認識については、BCCSkeltonでプログラミングをしていた時からSAPI 5で音声合成と一緒に提供されていたことは知っていました。しかしこの機能を必要としなかったので、好奇心からこのサンプルをコンパイルしましたが、認識精度が低いので興味を失っていました。(マイクが遠かったり、その精度も影響していたかも?)
今日日(キョウビ)の音声認識はもっとスマートですし、精度も高いみたいですね。以下のプログラムはWebで拾ったサンプルを合成し、WPFでウィンドウプログラム(注)に改造したものです。マイクが近いか、遠いかでも認識精度が大きく違うみたいです。
注:今回はまだ使ったことのなかったDockPanelを使ってみました。これを使うと上下左右に振り分けが可能になります。ただし、「下(Bottom)」を選択すると、更に左右に指定することはできないようです。
先ず、インストールされている音声認識エンジンを確認し、どれを使うか聞いてきます。(選択しないとずーーーーーっとこのループを繰り返します。プログラムをキャンセルで終了してもよいです。)
その後はマイクに向かって(近づけた方がよく認識されます)話しかけてください。文法に沿って文章を推測し、その後確定(認識)します。いっぱい表示される推定文がうざい、と感じたら「推定不要」ボタンを押してください。認識された分だけだとプロセスが分からない、という場合は「推定必要」ボタンを押しましょう。
【SpeechRecognitionWin.cs】
///////////////////////////////////////////////
//SpeechRecognitionEngineのテスト-Windows版
///////////////////////////////////////////////
using System;
using System.Windows;
using System.Windows.Controls; //コントロールを利用する為
//using System.Windows.Media.Imaging; //BitmapFrameを使用する為(解説:今回はプログラムアイコンを使用)
using System.Speech.Recognition; //音声認識エンジン-System.Speech.dll
namespace SpeechRecognitionWin
{
///////////////////////////
//エントリーポイントクラス
///////////////////////////
class MainApp
{
[STAThread]
public static void Main()
{
MainWindow mwnd = new MainWindow();
Application ap = new Application(); //解説:WPFでは明示的にインスタンスを作る必要がある
ap.Run(mwnd);
}
}
public partial class MainWindow : Window
{
//MainWindowクラスの変数
SpeechRecognitionEngine recEng; //音声認識エンジン
TextBox TBox; //テキストボックス
ListBox LBox; //リストボックス
Button extBtn, hypoBtn; //終了、推定表示可否ボタン
bool AddHypo = true; //推定処理の表示フラグ
//コンストラクター
public MainWindow()
{
//this.Icon = BitmapFrame.Create(new Uri("Icon.ico", UriKind.Relative)); //同じフォールダーのIcon.icoを読んで使用する(解説:今回はプログラムアイコンを使います。)
this.WindowStyle = WindowStyle.ThreeDBorderWindow;
this.Background = SystemColors.WindowBrush;
this.WindowStartupLocation = WindowStartupLocation.CenterScreen;
this.Width = 525;
this.Height = 350;
this.ResizeMode = ResizeMode.CanResizeWithGrip;
this.ShowActivated = true; //アクティブ状態で初期表示するかどうか-規定値 true
this.ShowInTaskbar = true; //タスクバ-にボタン表示するか否か
this.Title = "Speech Recognition";
this.Loaded += Window_Loaded;
}
//WM_CREATE時処理
private void Window_Loaded(object sender, EventArgs e)
{
//コントロールの設定
InitControls();
/*解説: 音声認識を行うには、
(1)音声認識エンジンのインスタンスを作成する
(2)ディクテーション用辞書を作成する
(3)
*/
//解説:インストールされていないと致命的エラーになりますが、Windowsのロケールのエンジンはあると思います。
//インストールされている音声認識エンジンでインスタンスを生成
recEng = new SpeechRecognitionEngine(SelectEngine());
//DictationGrammar (ディクティション用の文法)の追加
recEng.LoadGrammar(new DictationGrammar());
try
{
//音声認識エンジンへの入力設定(解説:マイクなどの入力デバイスがないと致命的エラーとなります。)
recEng.SetInputToDefaultAudioDevice();
//非同期音声認識を継続、複数音声認識を実行する
//解説:非同期音声認識操作、複数音声認識(一回だけで終了しない)を参照してください。
recEng.RecognizeAsync(RecognizeMode.Multiple);
}
catch
{
MessageBox.Show("音声標準出力がありません", "エラー", MessageBoxButton.OK, MessageBoxImage.Error);
}
//音声認識イベントハンドラー
recEng.SpeechRecognized += new EventHandler<SpeechRecognizedEventArgs>(recEng_SpeechRecognized);
//recEng.SpeechRecognized += recEng_SpeechRecognized; //解説:表示方法が異なるだけで↑と同じ。
//音声推定イベントハンドラー
recEng.SpeechHypothesized += new EventHandler<SpeechHypothesizedEventArgs>(recEng_SpeechHypothesized);
//recEng.SpeechHypothesized += recEng_SpeechHypothesized; //解説:表示方法が異なるだけで↑と同じ。
}
//インストールされた音声認識エンジンを表示し、選択させる
public RecognizerInfo SelectEngine()
{
//取り敢えず、インストールされた全音声認識エンジンをリストボックスに表示
foreach(RecognizerInfo ri in SpeechRecognitionEngine.InstalledRecognizers())
{
LBox.Items.Add(ri.Culture.Name);
}
//音声認識エンジンの選択
RecognizerInfo info = null;
while(info == null) //選択しない限り終了しない
{
//インストールされた音声認識エンジンを順に表示
foreach(RecognizerInfo ri in SpeechRecognitionEngine.InstalledRecognizers())
{
MessageBoxResult result = MessageBox.Show(
"以下のエンジンが見つかりました。\r\n\r\n" +
ri.Name + " : " + ri.Culture.Name +
"\r\n\r\nこれを選択しますか?(キャンセル-終了)", "音声認識エンジン",
MessageBoxButton.YesNoCancel, MessageBoxImage.Question);
if(result == MessageBoxResult.Yes) //選択
{
info = ri;
MessageBox.Show(ri.Culture.Name + " で音声認識を開始します。", "通知", MessageBoxButton.OK, MessageBoxImage.Information);
break;
}
else if(result == MessageBoxResult.Cancel) //キャンセルは終了
{
Close();
}
}
}
return info;
}
//コントロールの設定
public void InitControls()
{
//グリッド(Grid)を作成
Grid grid = new Grid();
//縦二列を作成・定義
ColumnDefinition colL = new ColumnDefinition();
ColumnDefinition colR = new ColumnDefinition();
colL.Width = new GridLength(this.Width / 2, GridUnitType.Pixel);
//colL.Width = GridLength.Auto;
colR.Width = GridLength.Auto;
grid.ColumnDefinitions.Add(colL);
grid.ColumnDefinitions.Add(colR);
//テキストボックスを作成
TBox = new TextBox();
//位置、フォントサイズとその他設定
TBox.Margin = new Thickness(5, 5, 5, 5);
TBox.FontSize = 12;
TBox.HorizontalAlignment = HorizontalAlignment.Left;
TBox.TextWrapping = TextWrapping.NoWrap;
TBox.HorizontalScrollBarVisibility = ScrollBarVisibility.Visible;
TBox.VerticalScrollBarVisibility = ScrollBarVisibility.Visible;
//グリッド左列にをテキストボックスを登録
Grid.SetColumn(TBox, 0); //Gridクラスの静的メソッドである点に注意
grid.Children.Add(TBox);
//ドックパネル(DockPanel)を作成
//解説:今回はまだ使ったことのなかったDockPanelにしてみました。
DockPanel dPanel = new DockPanel();
//ラベルを生成
Label lbl = new Label();
lbl.Content = "このPCにインストールされている音声認識エンジン";
lbl.HorizontalAlignment = HorizontalAlignment.Center;
DockPanel.SetDock(lbl, Dock.Top); //DockPanelクラスの静的メソッドである点に注意
dPanel.Children.Add(lbl);
//リストボックスを生成(解説:これも使ったことが無かったので無理矢理使ってみました。)
LBox = new ListBox();
LBox.SelectionMode = SelectionMode.Single;
LBox.Margin = new Thickness(10, 10, 10, 10);
LBox.HorizontalAlignment = HorizontalAlignment.Stretch; //解説:伸びることは無いですね。
LBox.VerticalAlignment = VerticalAlignment.Stretch;
DockPanel.SetDock (LBox, Dock.Top); //DockPanelクラスの静的メソッドである点に注意
dPanel.Children.Add(LBox);
//終了ボタンを生成(解説:「縦の底」を選ぶと下から挿入します。)
extBtn = new Button();
extBtn.Width = 60;
extBtn.Height = 24;
extBtn.Margin = new Thickness(10, 10, 10, 10); //構造体 Thickness(左, 上, 右, 下)
extBtn.HorizontalAlignment = HorizontalAlignment.Stretch; //解説:結局中央(Center)になります。
extBtn.VerticalAlignment = VerticalAlignment.Bottom; //解説:「縦の底」
extBtn.Content = "終了";
extBtn.Click += extBtn_Click;
DockPanel.SetDock (extBtn, Dock.Bottom); //DockPanelクラスの静的メソッドである点に注意
dPanel.Children.Add(extBtn);
//推定ボタンを生成
hypoBtn = new Button();
hypoBtn.Width = 60;
hypoBtn.Height = 24;
hypoBtn.Margin = new Thickness(10, 10, 10, 10); //構造体 Thickness(左, 上, 右, 下)
hypoBtn.HorizontalAlignment = HorizontalAlignment.Stretch; //解説:結局中央(Center)になります。
hypoBtn.VerticalAlignment = VerticalAlignment.Bottom;
hypoBtn.Content = "推定不要"; //解説:初期値(AddHypoが真の場合)
hypoBtn.Click += hypoBtn_Click;
DockPanel.SetDock (hypoBtn, Dock.Bottom); //DockPanelクラスの静的メソッドである点に注意
dPanel.Children.Add(hypoBtn);
//グリッド右列にドックパネルを登録
Grid.SetColumn(dPanel, 1); //Gridクラスの静的メソッドである点に注意
grid.Children.Add(dPanel);
//MainWindowにgridを追加
this.Content = grid;
}
//終了処理
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
//終了時に音声認識エンジンを開放する。
//解説:一応はガーベージコレクターが解放するようですが、「注意」に明示的開放が必要とあります?
recEng.Dispose();
}
//認識時の処理
void recEng_SpeechRecognized(object sender, SpeechRecognizedEventArgs e)
{
this.TBox.Text = "認識:" + e.Result.Text + "(" + e.Result.Confidence + ")\r\n" + this.TBox.Text;
}
//推定時の処理
void recEng_SpeechHypothesized(object sender, SpeechHypothesizedEventArgs e)
{
if(AddHypo) //解説:AddHypoが真の際に推定値を表示する
this.TBox.Text = "推定:" + e.Result.Text + "("+ e.Result.Confidence + ")\r\n" + this.TBox.Text;
}
//推定ボタンクリック時の処理
void hypoBtn_Click(object sender, RoutedEventArgs e)
{
AddHypo = !AddHypo; //解説:所謂トグル処理
if(AddHypo)
hypoBtn.Content = "推定不要";
else
hypoBtn.Content = "推定必要";
}
//終了ボタンクリック時の処理
void extBtn_Click(object sender, RoutedEventArgs e)
{
Close();
}
}
}
今回もWPFでウィンドウプログラムを書きましたが、次は【WPF】シリーズの総括をしようと思っています。