Bakulog

獏の夢日記的な何か。

AmiVoice SP APIでAmiVoice SPが起動できない?

音声認識ソフト"AmiVoice SP2"を自作ソフトに組み込む際に一瞬ハマったので公開。

AmiVoice SP2は日本語の音声をマイクやwavファイルから入力し、それをテキストに変換できる強力な有料ソフトです。いわゆる音声認識ソフトと呼ばれるものですね。

AmiVoice SP2は一般価格だと2万円程度のソフトです。無償の音声認識ライブラリとしてはJuliusrospeex等もあるんですが、AmiVoiceのメリットとしてGUIがしっかりしてる事に加えてWindows用のAPI(APIは無償公開)が整っているというメリットもあるので、個人的にはWindows上で遊ぶ場合ならAmiVoice SP2を推します。

 

で…このAmiVoice SP2 APIですが、チュートリアルを試したら失敗しました。具体的にはAmiVoice SP2の自動起動がナゼか失敗するようです。実行環境は以下の通りです。

原因はよくわかってない(ネットで調べても何も出てこない)んですが、作ったプログラムの実行前にあらかじめ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使ってるあたりがダサいですが、まあ応急処置ということで…。