音声認識ソフト"AmiVoice SP2"を自作ソフトに組み込む際に一瞬ハマったので公開。
AmiVoice SP2は日本語の音声をマイクやwavファイルから入力し、それをテキストに変換できる強力な有料ソフトです。いわゆる音声認識ソフトと呼ばれるものですね。
AmiVoice SP2は一般価格だと2万円程度のソフトです。無償の音声認識ライブラリとしてはJuliusやrospeex等もあるんですが、AmiVoiceのメリットとしてGUIがしっかりしてる事に加えてWindows用のAPI(APIは無償公開)が整っているというメリットもあるので、個人的にはWindows上で遊ぶ場合ならAmiVoice SP2を推します。
で…このAmiVoice SP2 APIですが、チュートリアルを試したら失敗しました。具体的にはAmiVoice SP2の自動起動がナゼか失敗するようです。実行環境は以下の通りです。
- Visual Studio 2013 Community
- Windows 8.1 通常版 日本語版
- C# + Windows Formでサンプルコードそのまま使用
- ターゲットプラットフォームは.NET Framework 4.5
原因はよくわかってない(ネットで調べても何も出てこない)んですが、作ったプログラムの実行前にあらかじめAmiVoice SPを起動しておかないとダメみたいです。ローカルビルド無しでサンプルに同梱のexeファイルを実行するとAmiVoiceが起動されてない場合に自動で起動してくれるので、ビルド環境の問題のようですが…
サンプル版のコードはVisual C# 2010とかいう古代兵器で作られてるみたいなので、多分.NETとかコンパイラのバージョン関係の問題なんでしょうかね。
仕方ないので、私のコードではAPIに頼らずProcessを使って無理やり起動処理を代行するコードを書きました。ポイントは初期化中のAmiVoiceにプロセス間通信をしようとすると吐かれるエラーを拾い、OKが貰えるまで接続試行を繰り返すところです。
using System; using System.Linq; using System.Threading.Tasks; using System.Diagnostics; using System.Windows.Forms; namespace AmiVoiceFormControlLib { public partial class AxVInput : Form { const string processName = "AmiVoiceSP"; const string processPath = @"C:\Program Files (x86)\AmiVoiceSP\AmiVoiceSP.exe"; enum AmiVoiceFailedExCode { NowInitializing = 1, AlreadyConnected = 2, CannotConnect = 3, CannotExecute = 4, NotInstalledAmiVoice = 5, FailedToInitializeActiveXControl = 6, ConnectionRejectedByAmiVoice = 99 } public AxVInput() { InitializeComponent(); if(Process.GetProcesses().Any(p => p.ProcessName == processName)) { //すでにAmiVoiceSP.exeが起動してるケース axVInput6Ctrl1.StartEngine("", "", ""); } else { //AmiVoiceSPを起動する必要があるケース Process.Start(processPath); TryConnectToAmiVoice(); } } private void TryConnectToAmiVoice() { if(Process.GetProcesses().Any(p => p.ProcessName == processName)) { axVInput6Ctrl1.StartEngine("", "", ""); } } private async void axVInput6Ctrl1_EngineStartFailedEx(object sender, AxVINPUT6CTRLLib._DVInput6CtrlEvents_EngineStartFailedExEvent e) { if ( e.nFailedCode == (int)AmiVoiceFailedExCode.NowInitializing || e.nFailedCode == (int)AmiVoiceFailedExCode.CannotExecute ) { //起動中アクセスの場合、待ってから出直す await Task.Delay(1000); TryConnectToAmiVoice(); } else { //上のエラー以外はすべて想定外とする MessageBox.Show(e.nFailedCode.ToString() + e.strMessage); } } private void axVInput6Ctrl1_EngineStartCompleted(object sender, EventArgs e) { //ここはチュートリアルを参考に書けばOK } private void axVInput6Ctrl1_ResultAccepted(object sender, AxVINPUT6CTRLLib._DVInput6CtrlEvents_ResultAcceptedEvent e) { //ここはチュートリアルを参考に書けばOK } private void AxVInput_FormClosing(object sender, FormClosingEventArgs e) { this.axVInput6Ctrl1.PauseRecognize(); this.axVInput6Ctrl1.Terminate(); } } }
Task.Delay使ってるあたりがダサいですが、まあ応急処置ということで…。