Gnu Emacs 25.1から導入されたDynamic Modulesを使ってオーディオファイルを再生する

本稿は[KLab Advent Calendar 2017](https://qiita.com/advent-calendar/2017/klab)の6日目の記事です.


# はじめに

[Gnu Emacs](https://www.gnu.org/software/emacs/) 25.1(以下Emacs)では新たにShared Libraryを動的にロードする機能が導入されました. この機能を使う事で, これまでは外部のプロセスを利用しないと実現できなかった事がEmacsのみで完結できるようになりました. 今回はその一例としてwavファイルを再生する簡単なDynamic Moduleを作ります.


# 下準備


## Emacsのビルド

まず, Emacsをビルドします. なぜなら, configure時に&#x2013;with-modulesオプションを付与した状態でビルドされたEmacsでなければこの機能を使う事ができないからです<sup><a id="fnr.1" class="footref" href="#fn.1">1</a></sup>.

macOS環境でEmacsをビルドする際のShell Scriptを以下に示します. なお, このsnippetはHacker's patchを適用しています. また, 各バージョンは2017年12月5日現在の最新のバージョンです.

```bash
EMACS_VERSION=25.3
PATCH_VERSION=6.8

## Emacsとpatchを取得して
curl -O http://ftp.gnu.org/pub/gnu/emacs/emacs-${EMACS_VERSION}.tar.gz
curl -O ftp://ftp.math.s.chiba-u.ac.jp/emacs/emacs-${EMACS_VERSION}-mac-${PATCH_VERSION}.tar.gz

## 伸張
tar zxvf emacs-${EMACS_VERSION}.tar.gz
tar zxvf emacs-${EMACS_VERSION}-mac-${PATCH_VERSION}.tar.gz

cd emacs-${EMACS_VERSION}

## パッチを適用し
patch -p 1 < ../emacs-${EMACS_VERSION}-mac-${PATCH_VERSION}/patch-mac
cp -r ../emacs-${EMACS_VERSION}-mac-${PATCH_VERSION}/mac mac
cp ../emacs-${EMACS_VERSION}-mac-${PATCH_VERSION}/src/* src
cp ../emacs-${EMACS_VERSION}-mac-${PATCH_VERSION}/lisp/term/mac-win.el lisp/term
cp nextstep/Cocoa/Emacs.base/Contents/Resources/Emacs.icns mac/Emacs.app/Contents/Resources/Emacs.icns

## ビルド
./configure --with-mac --with-modules --without-x  # ここでDynamic Moduleの機能を有効にする.
make -j16

sudo make install
```

ここで用意したEmacsのソースコードの一部を後で利用するので, インストール終わったからといって捨ててはいけません.


## 外部ライブラリの準備

自分で作るのが面倒なので, オーディオファイルを再生するために必要な外部ライブラリを用意します. 今回は[portaudio](http://www.portaudio.com/)と[libsndfile](http://www.mega-nerd.com/libsndfile/)を利用します.

macOS環境では手っ取り早くhomebrewを使ってインストールします. Windows環境下でもpacmanなどを利用すればお手軽にインストールできると思います.

```bash
brew install portaudio
brew install libsndfile
```


# Dynamic Moduleの作成

いよいよDynamic Moduleの開発に入っていきます.


## エントリーポイント

各Dynamic Moduleは extern "C" されたemacs-module-initという関数をエントリーポイントとします. ですので, この関数の中にDynamic Moduleの実装をしていくことになります. また, EmacsがロードするDynamic ModuleはGPL互換ライセンスである必要があります. その宣言をModule内で表すために, plugin\_is\_GPL\_compatibleというシンボルをexportしなければなりません.

```cpp
int plugin_is_GPL_compatible; // module-loadされる時にこの名称のシンボルがShared Library内に存在するかどうかを確認するためだけに必要.

int
emacs_module_init(emacs_runtime* ert)
{
  // Dynamic Moduleの処理を記述
}
```

struct emacs\_runtimeはEmacsのソースコード郡の中にある, src/emacs-module.h内で定義されています. そのため, このヘッダファイルへのパスを通すか, コピーしてDynamic Moduleのソースコードがあるディレクトリに持ってくるかした上で, #includeする必要があります.


## 自module用のemacs\_envを取得する.

emacs\_module\_initの引数で渡されるemacs\_runtime型の変数からemacs\_env型のオブジェクトを取得します. これはこのDynamic Module用で, 他のDynamic Moduleとは独立しています.

```cpp
auto env = ert->get_environment(ert);
```


## 関数の生成と束縛

このenvを利用してEmacs側から利用したいシンボルや関数を作っていきます.


### make\_function

関数を作るには, emacs\_envのmake\_functionを利用します.

```cpp
struct emacs_env
{
  emacs_value (*make_function) (emacs_env *env,
                                ptrdiff_t min_arity, // 受け取る引数の数の内 必須な物の数
                                ptrdiff_t max_arity, // 受け取る引数の数の最大値
                                // Emacs側から呼び出したい関数へのポインタ
                                emacs_value (*function) (emacs_env *env,
                                                           ptrdiff_t nargs, // argsの数
                                                           emacs_value args[], // 関数へ渡される引数
                                                           void *) EMACS_NOEXCEPT, // ユーザーデータ
                                const char *documentation, // 関数のドキュメント文字列
                                void *data); // ユーザーデータ
};
```

Emacs内で扱うシンボルや関数などあらゆる物はemacs\_valueという抽象的な型の元で実装されており, 基本的にはこのemacs\_value型でやりとりを行います. make\_functionも例に盛れず, Emacsから呼び出したい関数をfunction引数に渡すと, その他の引数と共にEmacsで利用できるようにemacs\_valueとして返されます. ただし, これだけだと, lambdaを作ったような物で名前がなく, Emacsから呼び出す事が難しいので名前を付けます.


### intern

新しいシンボルを取得するにはEmacs Lispと同様にintern<sup><a id="fnr.2" class="footref" href="#fn.2">2</a></sup>する必要があります. Dynamic Moduleからinternを呼び出すには, emacs\_envが持っているintern関数ポインタを介します.

```cpp
struct emacs_env
{
  emacs_value (*intern) (emacs_env *env,
                         const char *symbol_name); // internしたいシンボル名

};
```

これを呼び出す事で, symbol\_nameを持つシンボルとしてemacs\_valueを取得できます.


### funcall

internしたシンボルとmake\_functionで作成した関数を結びつけるにはfset<sup><a id="fnr.3" class="footref" href="#fn.3">3</a></sup>を使いますが, fset関数はemacs-module.h内には用意されていないので, emacs\_envのfuncall<sup><a id="fnr.4" class="footref" href="#fn.4">4</a></sup>関数ポインタを利用してfsetを呼び出します.

```cpp
struct emacs_env
{
  emacs_value (*funcall) (emacs_env *env,
                          emacs_value function, // funallで呼び出す関数
                          ptrdiff_t nargs, // funcall時, functionに渡される引数の数
                          emacs_value args[]); // funcall時, functionに渡される引数
};
```


### fset

fsetは頻繁に使う事になるので, 一つの関数として定義します.

```cpp
void
fset(emacs_env* env, emacs_value symbol, emacs_value function)
{
  auto fset = env->intern(env,
                          "fset");
  std::array<emacs_value, 2> args{{symbol,
                                   function}};
  env->funcall(env,
               fset,
               args.size(),
               args.data());
}
```

例えば, オーディオファイルへのパスを引数に受け取り, そのファイルのデータを再生するaudio-player-play-audioという関数を実装するには以下のようになります.

```cpp
// 関数の実体をどこかで定義.
emacs_value play_audio(emacs_env* env, ptrdiff_t nargs, emacs_value args[], void* user_data) EMACS_NOEXCEPT;

int
emacs_module_init(emacs_runtime* ert)
{
  auto env = ert->get_environment(ert);
  auto function = env->make_function(env,
                                     1, // 引数としてファイルパスを1つ取るので1
                                     1, // optional等は使わないのでmaxも1
                                     play_audio,
                                     "Play audio from file.",
                                     nullptr);
  auto symbol = env->intern(env,
                            "audio-player-play-audio");
  fset(env,
       symbol,
       function);
}
```


## provide

これで関数を生成しシンボルと結びつける事はできましたが, それだけではまだ足りません. Emacs Lispでpackageを作る時と同様にprovide<sup><a id="fnr.5" class="footref" href="#fn.5">5</a></sup>する必要があります. provideもfsetと同じようにinternでprovideのemacs\_valueを取得し, それをfuncallに渡して呼び出します.

```cpp
void
provide(emacs_env* env, const char* feature_name)
{
  auto provide = env->intern(env,
                             "provide");
  auto feature = env->intern(env,
                             feature_name);
  std::array<emacs_value, 1> args{{feature}};
  env->funcall(env,
               provide,
               args.size(),
               args.data());
}
```

上記のprovide関数を使ってaudio-playerという名前で登録します.

```cpp
int
emacs_module_init(emacs_runtime* ert)
{
  auto env = ert->get_environment(ert);
  // なんやかんや処理をして

  provide(env,
          "audio-player");

  return 0;
}
```

これをビルドしてsite-lispなどパスの通っている処にDynamic Moduleを置いてrequireすると各関数が呼び出せるようになります.

```emacs-lisp
(require 'audio-player)
(audio-player-play-audio "/path/to/your/audio_data.wav")
```


## 注意点


### Module名

この時作成されたDynamic Moduleは, macOSではlibaudio-player.dylib, LINUXなどPOSIX環境下ではllibaudio-player.soとなります. prefixのlibが付いた状態だとEmacsはそれをDynamic Moduleとして自動的に認識する事ができません. ですので, 接頭辞のlibを削除するか, module-loadで明示的にロードする必要があります.

またmacOS環境の場合, 拡張子dylibをEmacsはDynamic Moduleと認識しないため, .soに変更する必要もあります. これはmodule-file-suffixという変数で設定されている拡張子のみをDynamic Moduleとして認識するためです. なお, module-file-suffixの値をinit.elなどで".dylib"に変更しても読み込むことはないので大人しく拡張子を変更するか, やはりmodule-loadで明示的にロードする必要があります.


### 例外

EMACS\_NOEXCEPTが指定されている事からもわかる通り, Emacs側に例外を投げる事はできません. ですので, 例外はModule内部で全て処理する必要があります.


# むすび

今回作成したDynamic Moduleのソースコードは, 上記で説明していないmessage関数の呼び出し方や, 文字列の取得方法, オーディオデータの再生部分等も含めて[github repository](https://github.com/masfj/DynamicModuleSample)に全て置いてあります.

これまでは, 別途プロセスを作成しそのプロセスと通信して処理をしなければならなかった事も, Dynamic Moduleが実装された事でEmacsのプロセス内で完結させる事ができるようになり, Emacsの可能性がさらに広がる事となりました. 筆者は現在EmacsをVSTホストアプリケーションとするDynamic Moduleを作成中です. これによってVST Pluginの実装からデバッグまで全てEmacsだけで完結できるようになり益々開発が捗ります.

Emacsは本当に素晴らしいエディタですね.

それでは, 素敵なEmacs Lifeを!

## 脚注

<sup><a id="fn.1" class="footnum" href="#fnr.1">1</a></sup> <https://www.gnu.org/software/emacs/manual/html_node/elisp/Dynamic-Modules.html>

<sup><a id="fn.2" class="footnum" href="#fnr.2">2</a></sup> <https://www.gnu.org/software/emacs/manual/html_node/elisp/Creating-Symbols.html#index-intern-577>

<sup><a id="fn.3" class="footnum" href="#fnr.3">3</a></sup> <https://www.gnu.org/software/emacs/manual/html_node/elisp/Function-Cells.html#index-fset-897>

<sup><a id="fn.4" class="footnum" href="#fnr.4">4</a></sup> <https://www.gnu.org/software/emacs/manual/html_node/elisp/Calling-Functions.html#index-funcall-866>

<sup><a id="fn.5" class="footnum" href="#fnr.5">5</a></sup> <https://www.gnu.org/software/emacs/manual/html_node/elisp/Named-Features.html#index-provide-1052>

著者: m13o
Emacs 25.3.1 (Org mode 9.1.3)