MMDAgentについてまとめる FST編

# この記事について
MMDAgentを利用する・開発する上でのあれこれをまとめた記事．前回記事からの続きになっているのでそちらも参照されたし．
前回：[MMDAgentについてまとめる 音声認識・音声合成編](https://qiita.com/m-masaki72/items/c850710186e13d69132d)
次回：まだです

# MMDAgentの対話管理部
MMDAgentの対話管理はFST(finite state transducer)によって行われている．プラグインとしてはPlugin_VIManagerというスレッドによって行われている．FSTによる対話管理による特徴を以下に挙げる．

* （乱数がなければ）一意に動作する
*  入力と出力の対として動作がわかりやすい
*  対話の流れを考慮した動作が可能
*  記述を複雑化することでより高度な対話を記述できる

# FSTとは
FST(finite state transducer)は有限状態トランスデューサのことである．数学的な話だと，有限オートマトンの遷移に出力を加えたものである．FSTの模式図を載せる．
![fst_2.png](https://qiita-image-store.s3.amazonaws.com/0/125382/1c6f7a25-5243-6e4b-8d68-001071e03e14.png)

# FSTによる対話管理
FSTによる対話管理は以下図のようになる．
注目すべき点は音声対話の動作を「入力/出力」の対によって定義する点だ．

図の例では，状態0において「年，は」など年齢に関する質問をされた際には，「秘密です」と合成するような動作を定義している．
また状態1と2の間では，空動作による動作を定義している．これは状態1についたらすぐに指定した動作をするような命令である．これによって先ほどの命令と併せて，「秘密です」と発話したあとすぐに人差し指を唇に当てる動作をするものである．
以上の状態0から2すべてによって，一つの質問に対する動作が行われている．

![b5b3339740d55523bdc82dda77ab4903.png](https://qiita-image-store.s3.amazonaws.com/0/125382/51193e73-952c-1ebc-1ff2-1e5e12524cfb.png)

FSTの動作による特徴であるが，小さな動作から大きい動作まで柔軟に表現できる．これはMMDAgentがFSTを採用している理由のひとつでもあると考えられる．
小さな動作はひとつの発話，ひとつの認識，ひとつのモーションという単位である．これらを積み上げていくことによって，1つの発話と動作，1つのモーションの連続，1つの挨拶群といったことまで表現可能である．

# MMDAgentにおけるFSTの記述
FSTのコマンドを解説する．FSTで利用できるコマンドと使用例に関してまとめる．
基本的には公式のFSTより引用．

### 基本的な文法
```text
# シャープ以下はコメントになる
# コマンドはスペース区切りの4つのフィールドを一対とした命令を記述する
# 1st field: 状態番号
# 2nd field: 遷移先状態番号
# 3rd field: 命令がトリガされるコマンド
# 4th field: 実行されるコマンド

# 例えば以下の例ではというコマンド
# 1st field: 状態が1にいるときに
# 2nd field: 実行されて状態2へと繊維する
# 3rd field: こんにちは，がRECOGされた場合
# 4th field: やあ，と発話する

1 2 RECOG_EVENT_STOP|こんにちは SYNTH_START|mei|mei_voice_normal|やあ

# 命令にはこのようなRECOGなど様々なコマンドが定義されている
# また<eps>と記述することで空遷移，なにもしないことを許している．
# 以下のコマンドでは先ほの命令と同じだが，発話しないように<eps>を設定している．

1 2 RECOG_EVENT_STOP|こんにちは <eps>
```

### よく使うコマンド
```text
# RECOG_EVENT_STOP|***
# なにかを認識した場合に使うコマンド
# 使用例
1    41   RECOG_EVENT_STOP|自己紹介  SYNTH_START|mei|mei_voice_normal|メイと言います。

# SYNTH_START|<モデルのエイリアス>|<発話の種類>|***
# なにかを発話するときに使うコマンド
# 各自設定したモデルのエイリアスとojtファイルで指定した声のパラメータが必要
# 使用例
1    41   RECOG_EVENT_STOP|自己紹介  SYNTH_START|mei|mei_voice_normal|メイと言います。

# MODEL_ADD|<モデルのエイリアス>|<pmdのパス>|<座標>
# MMDモデル（PMD形式オンリー）を追加するコマンド
11   12  <eps>          MODEL_ADD|mei|Model\mei\mei.pmd|0.0,0.0,-14.0

# MOTION_ADD|<モデルエイリアス>|<モーションのエイリアス>|<モーションファイルのパス>|(FULL or PART)|(ONCE or LOOP)|(ON or OFF for smooth)|(ON or OFF for repos)
# モデルに対してモーションファイルを流し込むコマンド
# 使用例
1    2    TIMER_EVENT_STOP|idle3              MOTION_ADD|mei|idle|Motion\mei_idle\mei_idle_think.vmd|PART|ONCE
```

### よく使うテクニック

```text
# EVENT_STOPコマンドを利用することで，順番に実行することができる．
# 以下の例では発話，動作のEVENT_STOPをトリガに設定することで発話がおわったら，動作がおわったら次になにをするといった動作を行っている
1    31   RECOG_EVENT_STOP|こんにちわ         SYNTH_START|mei|mei_voice_normal|こんにちは。
31   32   <eps>                               MOTION_ADD|mei|action|Motion\mei_greeting\mei_greeting.vmd|PART|ONCE
32   2    SYNTH_EVENT_STOP|mei                <eps>

# タイマーを使った処理
# 以下の例ではタイマー使ってidleモーションの繰り返しを行っている．
# それぞれ，20秒，40秒，60秒ごとのidleモーションを記述している．
2    21   <eps>                               TIMER_START|idle1|20
21   22   TIMER_EVENT_START|idle1             TIMER_START|idle2|40
22   23   TIMER_EVENT_START|idle2             TIMER_START|idle3|60
23   1    TIMER_EVENT_START|idle3             VALUE_SET|random|0|100
1    1    RECOG_EVENT_START                   MOTION_ADD|mei|listen|Expression\mei_listen\mei_listen.vmd|PART|ONCE
1    1    TIMER_EVENT_STOP|idle1              MOTION_ADD|mei|idle|Motion\mei_idle\mei_idle_boredom.vmd|PART|ONCE
1    1    TIMER_EVENT_STOP|idle2              MOTION_ADD|mei|idle|Motion\mei_idle\mei_idle_touch_clothes.vmd|PART|ONCE
1    2    TIMER_EVENT_STOP|idle3              MOTION_ADD|mei|idle|Motion\mei_idle\mei_idle_think.vmd|PART|ONCE
```

# まとめ
MMDAgentのFST記述方に関してまとめた．
実際に作るとなると色々な作り方があるので，実際に作ってみるor作られたものを参考にしていくのが良いと思われる．

# 参考文献
[CommandReference · hkrn/MMDAI Wiki - GitHub](https://github.com/hkrn/MMDAI/wiki/CommandReference)
[MMDAgent モデル操作コマンド](http://idehideout.blog6.fc2.com/category6-3.html)
[MMDAgent シナリオコマンド](http://idehideout.blog6.fc2.com/blog-entry-812.html)

# 付録．MMDAgentコマンド一覧
```text
# Model
# MODEL_ADD|(model alias)|(model file name)|(x position),(y position),(z position)|(x rotation),(y rotation),(z rotation)|(ON or OFF for cartoon)|(parent model alias)|(parent bone name)
# MODEL_CHANGE|(model alias)|(model file name)
# MODEL_DELETE|(model alias)
# MODEL_EVENT_ADD|(model alias)
# MODEL_EVENT_CHANGE|(model alias)
# MODEL_EVENT_DELETE|(model alias)
#
# Motion
# MOTION_ADD|(model alias)|(motion alias)|(motion file name)|(FULL or PART)|(ONCE or LOOP)|(ON or OFF for smooth)|(ON or OFF for repos)
# MOTION_ACCELERATE|(model alias)|(motion alias)|(speed)|(duration)|(specified time for end)
# MOTION_CHANGE|(model alias)|(motion alias)|(motion file name)
# MOTION_DELETE|(mpdel alias)|(motion alias)
# MOTION_EVENT_ADD|(model alias)|(motion alias)
# MOTION_EVENT_ACCELERATE|(model alias)|(motion alias)
# MOTION_EVENT_CHANGE|(model alias)|(motion alias)
# MOTION_EVENT_DELETE|(model alias)|(motion alias)
#
# Move and Rotate
# MOVE_START|(model alias)|(x position),(y position),(z position)|(GLOBAL or LOCAL position)|(move speed)
# MOVE_STOP|(model alias)
# MOVE_EVENT_START|(model alias)
# MOVE_EVENT_STOP|(model alias)
# TURN_START|(model alias)|(x position),(y position),(z position)|(GLOBAL or LOCAL position)|(rotation speed)
# TURN_STOP|(model alias)
# TURN_EVENT_START|(model alias)
# TURN_EVENT_STOP|(model alias)
# ROTATE_START|(model alias)|(x rotation),(y rotaion),(z rotation)|(GLOBAL or LOCAL rotation)|(rotation speed)
# ROTATE_STOP|(model alias)
# ROTATE_EVENT_START|(model alias)
# ROTATE_EVENT_STOP|(model alias)
#
# Sound
# SOUND_START|(sound alias)|(sound file name)
# SOUND_STOP|(sound alias)
# SOUND_EVENT_START|(sound alias)
# SOUND_EVENT_STOP|(sound alias)
#
# Stage
# STAGE|(stage file name)
# STAGE|(bitmap file name for floor),(bitmap file name for background)
#
# Light
# LIGHTCOLOR|(red),(green),(blue)
# LIGHTDIRECTION|(x position),(y position),(z position)
#
# Camera
# CAMERA|(x position),(y position),(z position)|(x rotation),(y rotation),(z rotation)|(distance)|(fovy)|(time)
# CAMERA|(motion file name)
#
# Speech recognition
# RECOG_EVENT_START
# RECOG_EVENT_STOP|(word sequence)
# RECOG_MODIFY|GAIN|(ratio)
# RECOG_MODIFY|USERDICT_SET|(dictionary file name)
# RECOG_MODIFY|USERDICT_UNSET
# RECOG_MODIFY|CHANGE_CONF|(config file name)
#
# Speech synthesis
# SYNTH_START|(model alias)|(voice alias)|(synthesized text)
# SYNTH_STOP|(model alias)
# SYNTH_EVENT_START|(model alias)
# SYNTH_EVENT_STOP|(model alias)
# LIPSYNC_START|(model alias)|(phoneme and millisecond pair sequence)
# LIPSYNC_STOP|(model alias)
# LIPSYNC_EVENT_START|(model alias)
# LIPSYNC_EVENT_STOP|(model alias)
#
# Variable
# VALUE_SET|(variable alias)|(value)
# VALUE_SET|(variable alias)|(minimum value for random)|(maximum value for random)
# VALUE_UNSET|(variable alias)
# VALUE_EVAL|(variable alias)|(EQ or NE or LE or LT or GE or GT for evaluation)|(value)
# VALUE_EVENT_SET|(variable alias)
# VALUE_EVENT_UNSET|(variable alias)
# VALUE_EVENT_EVAL|(variable alias)|(EQ or NE or LE or LT or GE or GT for evaluation)|(value)|(TRUE or FALSE)
# TIMER_START|(count down alias)|(value)
# TIMER_STOP|(count down alias)
# TIMER_EVENT_START|(count down alias)
# TIMER_EVENT_STOP|(count down alias)
#
# Plugin
# PLUGIN_ENABLE|(plugin name)
# PLUGIN_DISABLE|(plugin name)
# PLUGIN_EVENT_ENABLE|(plugin name)
# PLUGIN_EVENT_DISABLE|(plugin name)
#
# Other events
# DRAGANDDROP|(file name)
# KEY|(key name)
#
# Other commands
# EXECUTE|(file name)
# KEY_POST|(window class name)|(key name)|(ON or OFF for shift-key)|(ON or OFF for ctrl-key)|(On or OFF for alt-key)

```