サブモジュール組み込み機能は Plugin_AnyScript が提供しています。利用時はこのプラグインが有効になっているか確かめてください。
サブモジュールの組み込み #
Python スクリプトを含む任意のアプリケーションをサブモジュールとして MMDAgent-EX に組み込んでMMDAgent-EX の能力を拡張することができます。アプリケーションは子プロセスとして MMDAgent-EX 起動時に起動され、終了と同時に終了します。 音声対話をためす (Python)のページ、および ChatGPTを組み込む例で用いたのはこの方法です。
ここではサブモジュールとして組み込む方法について概説します。
Plugin_AnyScript #
サブモジュールの組み込みは Plugin_AnyScript で実現されています。 Plugin_AnyScript プラグインはアプリケーションを子プロセスとして起動・接続するプラグインです。Plugin_AnyScript によって起動された子プロセスは、標準入出力がMMDAgent-EX のメッセージキューと直接接続されます:
- 子プロセスの標準出力は、そのままメッセージとして MMDAgent-EX へ発行する
- MMDAgnet-EX から発行されるメッセージは、全て子プロセスの標準入力に与えられる
音声対話をためす (Python)で示したのは対話を制御する使い方でしたが、そのほかにも、
- 他の音声合成エンジンを使う: プロセスは
SYNTH_START
メッセージを標準入力から受け取り、音声合成を行ってオーディオ出力する(デフォルトの Plugin_Open_JTalk は disable する) - 他の音声認識エンジンを使う:プロセスは音声認識を行い、その結果を
RECOG_EVENT_STOP|認識結果
の形で標準出力へ出力する(デフォルトの Plugin_Julius は disable する) - カメラを追加する:カメラの検出結果に従って
MOTION_ADD
メッセージを標準出力へ送ることで、カメラに反応するモーションを挟み込む
といったような拡張が可能です。
プログラム作成上の注意 #
アプリケーションやスクリプトをサブモジュールとして組み込む際は、必ずそのプロセスの 標準出力のバッファリングを OFF にしてください。バッファリングが ON になっていると、出力したメッセージが即座に MMDAgent-EX に吐き出されず、処理が行われなかったり遅延したりします。
Python スクリプトの場合、起動時オプションに
-u
をつけることでバッファリングをOFFにできます。それ以外の場合は、1行出力するごとに明示的に出力をフラッシュするようプログラムを作成してください。
サブモジュール登録 #
サブモジュールは .mdf で登録します。サブモジュールとして起動するコマンドを指定します。=
から行末までの全てが起動コマンドとして使われます。コマンド中に空白があっても問題ありません。
パスの指定には特に注意してください。
- 実行バイナリはフルパスでもコマンドとしても指定できます。フルパスで指定された場合、その実行バイナリを実行します。コマンドとして指定された場合、Windows では
CreateProcessA
関数の仕様に従って実行バイナリが探され、macOS / Linux ではexecvp()
の仕様に従って環境変数PATH
から実行バイナリが探されます。 - 実行時のカレントディレクトリは、その .mdf の置いてあるフォルダです。ファイル名等を引数で指定する場合は注意してください。
# example 1: full-path on Windows
Plugin_AnyScript_Command=C:\Program Files\Python310\python.exe -u test.py
# example 2: command on macOS/Linux
Plugin_AnyScript_Command=python3 -u test.py
複数のモジュールを指定する場合は以下のように書いてください。最大で10個まで指定できます。
Plugin_AnyScript_Command1=...
Plugin_AnyScript_Command2=...
起動時の動作 #
サブモジュールとして指定されたアプリケーションは、MMDAgent-EX 起動時に MMDAgent-EX の子プロセスとして起動されます。また、MMDAgent-EX の終了と同時に終了します。
標準入力に送られるメッセージの選択 #
サブモジュールの標準入力には、MMDAgent-EX 内を流れるすべてのメッセージが送られます。送られるメッセージの種類の選択等はできません。モジュール側で必要に応じてメッセージを取捨選択・処理するよう処理を記述してください。
なお、デフォルトでは標準入力で受け取れるのは「メッセージ」のみですが、加えてすべての動作ログまで受け取りたい場合は、以下を .mdf に指定してください。
Plugin_AnyScript_AllLog=true
動作例 #
以下は天気予報を取得して答えるモジュールの作成例です。音声認識結果の RECOG_EVENT_STOP
メッセージは MMDAgent-EX 本体だけでなく全てのモジュールにも送られるので、仮に .fst スクリプトが「天気」という発話に答えられない場合でも、このようにサブモジュールが応答するよう作ることができます。
def query_weather():
# 天気予報を取得する
# 取得した予報をもとに応答文を作る
return(response)
if __name__ == "__main__":
while True:
input_line = input().strip()
if input_line.startswith("RECOG_EVENT_STOP|天気"):
response = query_weather()
print(f"SYNTH_START|0|mei_voice_normal|{response}")