MMDAgent(旧式)でのプラグイン作成方法[2024年度版]

## はじめに
MMDAgent(旧式)のプラグイン作成についての資料が古く、VS2022ではエラーが頻繁に起こり(MMDAgent-EXへの移行のため更新されない)、尚且つ解説記事などがほぼないので書きました。

やり方だけ知りたい人は下まで飛ばし読みしてください。

また、ソースコードも載せていますので雛形をそのまま使っても大丈夫です。
(使う際は公式のReadme.txt、copyright等をしっかり読んでください。)

https://github.com/shimaf4979/MMDAgent_Plugin_Sample

## MMDAgentとは？
公式サイトを閲覧してください。
名古屋工業大学国際音声技術研究所によって作成されたオープンソースの音声インタラクション構築ツールキットだそうです。

http://www.udialogue.org/ja/

> MMDAgent は音声認識や音声合成の実行に必要なモデルや定義・設定ファイルを読み込み、3D オブジェクトを OpenGL でレンダリングしながら、音声入力の録音からのリアルタイム音声認識や合成音声のリアルタイム出力を行い、FST 形式で書かれた対話スクリプトに従って 3D オブジェクトのモーション・移動等を行うことで音声インタラクションを実行するソフトウェアである。

## なぜ旧式を使うの？
分かりません。プラグインを自分で作成する能力・過去のC++との互換性の把握の練習といったところでしょうか。~~何も知りませんごめんなさい。~~

ネットを徘徊してもほとんど記事など見つからず、プラグイン抜き(exeファイルに変換しない限り)ではそもそもFSTと呼ばれる、「現在の状態」「次の状態」「トリガーとなる条件」「実行されるアクション」の記載に基づく有限状態機械記述言語（FSM Description Language）で、一般的にはかなりクリエイター向けの言語を扱うしかないので、これだけでもかなり感動できる体験が味わえますが、できる動作はかなり限定的になると思います。~~クリエイティブになろう！~~

MMDagent-EXの方では、pythonが使えたり、プラグインが豊富だったり、CGが綺麗だったり、時代の変化を感じられます。すごいので試してみてください。

## 導入編(VS2022)
ホームページにて基本的な導入がされています。この資料はVS2013での説明なので、アップデートがされておらず(EXへの移行)、VS0222でのビルドだと少しエラーを修正しなければなりませんでした。

http://www.udialogue.org/ja/encyclopedia-ja/developer-manual-jp.html

VS2022をダウンロードし、上記のpdfに従ってください。

手順的には
　ソースコードのダウンロード
→VS2022のダウンロード
→slnを開く。
→構成をWin32,Releaseに変更
→mainをスタートアッププロジェクトに変更
→ビルド
→( ^ω^ )エラーが大量に出現( ^ω^ )

## 追加で必要なこと:ビルドの依存関係の修正
![スクリーンショット 2024-10-23 3.04.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3738967/bcb739e7-2fcb-67a1-3837-0545c6bda070.png)

上記のようなエラーが出た場合、Plugin_TextAreaを右クリックして、ビルド依存関係の修正を行なってください。

![スクリーンショット 2024-10-23 3.00.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3738967/179931c0-0f55-b042-4923-4f697485d7a4.png)

以下二項目にチェックを

![スクリーンショット 2024-10-23 3.00.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3738967/6cb0882b-4190-7f72-2612-67ba751f1eef.png)

## 追加で必要なこと:エラー修正
### Library_Open_JTalkの問題
![スクリーンショット 2024-10-23 2.32.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3738967/137e35ba-625d-12df-7a6c-bc7a66060078.png)

`std::binary_function`は C++11 以降で非推奨になり、C++17 で削除されました。そのため、このコードを最新の C++ で使用する場合には、`std::binary_function`は不要で、代わりに比較関数オブジェクトを定義するか、`<functional>`をincludeするかの二択です。
変にincludeするよりかは前者を選びました。

### 解決前
![スクリーンショット 2024-10-23 2.34.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3738967/2292e710-9f69-ac5b-f903-c0cddc6d154c.png)

### 解決後
~~~C++:dictionary.c++
template <typename T1, typename T2>
struct pair_1st_cmp {
  bool operator()(const std::pair<T1, T2> &x1,
                  const std::pair<T1, T2> &x2)  {
    return x1.first < x2.first;
  }
};
}  // namespace
~~~

## プラグイン作成編
こちらも、先ほどの資料をもとに解説していきます。

手順的には
　新しいプロジェクトの追加
→WIN32の作成
→( ^ω^ )出てきませんでした( ^ω^ )

## dllを作るためのプロジェクトの作成

ソリューションを右クリックして、新しいプロジェクトを押してください。

![スクリーンショット 2024-10-23 3.18.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3738967/d12ae614-3fd7-4748-7a99-9dbcdbd9b686.png)

dllと検索して作成してください。

![スクリーンショット 2024-10-23 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3738967/b7e24b01-fb69-d48a-c25c-9f34a9893f96.png)

こちらを選択してください。

## コードの実装

`dllmain.c++`に以下のコードを貼り付けてください(資料とほとんど同じです)

~~~c++:dllmain.c++
/* definitions */
#ifdef _WIN32
#define EXPORT extern "C" __declspec(dllexport)
#else
#define EXPORT extern "C"
#endif /* _WIN32 */

#define LOGFILENAME "MessageLogMaster.txt"         /* ログファイルのファイル名 */
#define PLUGINLOGMESSAGE_NAME "LogMessageMaster"   /* プラグインの名前 */

/* ログファイル関係のメッセージタイプ（自由に内部メッセージタイプを設定できる） */
#define MMDAGENT_EVENT_FILEOPEN "LOGMESSAGE_EVENT_FILEOPEN"
#define MMDAGENT_EVENT_FILECLOSE "LOGMESSAGE_EVENT_FILECLOSE"

/* headers */
#include "MMDAgent.h"
#include <fstream>
#include <ctime>

/* variables */
static bool enable;
static std::ofstream ofs;
static time_t t;
static tm* x;


EXPORT void extAppStart(MMDAgent* mmdagent)
{
    if (mmdagent == nullptr) {
        printf("Error: mmdagent instance is null.\n");
        return;
    }

    enable = true;

    
    const char* fileName = LOGFILENAME;

   
    ofs.open(fileName, std::ios::out | std::ios::app);
    if (!ofs) {

        mmdagent->sendMessage(MMDAGENT_EVENT_FILEOPEN, "%s can not be opened!", fileName);
    }
    else {
       
        mmdagent->sendMessage(MMDAGENT_EVENT_FILEOPEN, "%s can be opened", fileName);

      
        t = time(0);
        char buf[32];
        ctime_s(buf, sizeof(buf), &t);

   
        ofs << buf;
        ofs << "[[Start]]" << std::endl;
    }
}


EXPORT void extProcMessage(MMDAgent* mmdagent, const char* type, const char* args)
{
    if (enable && type != nullptr) {
        
        ofs << type << "|" << (args ? args : "null") << std::endl;

    }
}


EXPORT void extAppEnd(MMDAgent* mmdagent)
{
    ofs << "[[End]]" << std::endl;
    ofs << std::endl;

 
    ofs.close();

}


EXPORT void extUpdate(MMDAgent* mmdagent, double deltaFrame)
{
  
}


EXPORT void extRender(MMDAgent* mmdagent)
{
   
}
~~~

設定は資料の通り以下の6つ行なってください。

①依存関係を選択
②C++のincludeディレクトリ
③リンカ設定-1
④リンカ設定-2
⑤マルチバイト文字に変更
⑥出力ファイルの設定

ビルドしてみると...

![スクリーンショット 2024-10-23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3738967/10f2cbbf-8ee1-1e31-0f3a-c88863bc196d.png)

上記のようにエラーが起きました。pch.h は、特定のコンパイルの高速化を目的として、頻繁に使用されるヘッダーファイルをまとめておくためのファイルで、入れておいて損はないと思われます。1行目に追加しましょう。
~~~C++:dllmain
/* definitions */
#include "pch.h"
~~~

もう一度、ビルドしてみると通りました。

これでデバッグを押すと...

![スクリーンショット 2024-10-23 4.07.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3738967/42dc97e7-120d-c16f-14d6-4aaf0af91c6f.png)

無事起動できましたね！

また、Logの出力も確認できました。
![スクリーンショット 2024-10-23 4.08.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3738967/804d3df9-9df0-3840-0ee8-e501738125f0.png)

以下がプラグインのソースコードでございます。

https://github.com/shimaf4979/MMDAgent_Plugin_Sample