REAPERのJSFXで遊んで見る

REAPERにはJSFXという、スクリプトを書くだけでVSTのようなオリジナルのプラグインエフェクトが作成できる機能があります。この使い方に関する情報はほとんど[公式ドキュメント](http://www.reaper.fm/sdk/js/js.php)だけなんですけど、ちょっと調べてみました。

VSTのような、と書きましたがいきなり音声信号処理をやるよりもまず視覚的にわかりやすいところから手を付けよう、ということでまずは画面描画系の処理から。音声信号処理は今度調べます。

## Hello World

まずはお約束のHello Worldです。

```
desc:my first effect

@init
hello = "Hello World";

@gfx
gfx_r = 0.2;
gfx_g = 0.8;
gfx_b = 0.6;

gfx_measurestr(hello, strw, strh);
gfx_x = gfx_w / 2 - strw / 2;
gfx_y = gfx_h / 2 - strh / 2;
gfx_drawstr(hello);
```

Hello Worldの文字がウィンドウ中央に表示されれば成功です。

### 説明

最初の行`desc:my first effect`はエフェクトの名前を指定するものです。`desc:`以降は何でも構いません。

`@init`以下のブロックは初期化処理を行う部分です。エフェクトを読み込んだ時やコードを編集して再コンパイルされた時等に呼び出されます。
このサンプルでは`hello`という変数に文字列を代入しています。**変数は事前に宣言する必要はありません。**

画面に描画する処理は`@gfx`以降に書きます。このコードブロックは1秒間に約30回実行され、この中でのみ`gfx_`で始まる関数や変数を使用することができます。このブロック外で`gfx_`で始まる関数や変数を使用すると、正しく動作しなかったり不明な動作をすることがあるので注意してください。

`gfx_r`,`gfx_g`,`gfx_b`は見ての通り色のR,G,Bを表す変数です。この値は後続する`gfx_`の描画系関数で描画される図形の色を指定します。
それぞれの値は0.0～1.0の間で指定します。

`gfx_drawstr()`は文字列を描画する関数です。描画位置は`gfx_x`,`gfx_y`で事前に指定します。
画面の幅、高さはそれぞれ`gfx_w`,`gfx_h`で取得できます。文字列の幅、高さは`gfx_measurestr(文字列, 幅を格納する変数, 高さを格納する変数)`で取得できます。
今回は画面の中央に表示したかったので、画面中央のX,Y座標を計算しました。画面の中心計算方法は`(画面幅/2, 画面高さ/2)`で求められますが、これだと画面の中央が文字列の左端になってしまうので、そこからさらに文字列の長さの半分だけ左に、文字列の高さの半分だけ上に移動します。

## マウスの位置

マウスの位置を取得するには`mouse_x`,`mouse_y`変数を使用します。

```
desc:my mouse effect

@gfx
gfx_r = gfx_g = gfx_b = 1; // white

hello = "<- This is mouse";
gfx_x = mouse_x;
gfx_y = mouse_y;
gfx_drawstr(hello);
```

マウスカーソルの位置に白い文字が描画されれば成功です。

`mouse_x`,`mouse_y`変数はそれぞれウィンドウ内の描画領域の左上からの相対座標を表します。**X座標は右が正、Y座標は下が正です。**
今回はこの値をそのまま`gfx_x`,`gfx_y`に入れています。`gfx_drawstr()`はこの位置から描画を開始するのでした。

## ランダムな値

ランダムな値を取り入れると勝手に動く面白いモノが作れそうです。

```
desc:random color

@gfx
gfx_r = rand(1);
gfx_g = rand(1);
gfx_b = rand(1);

hello = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
gfx_x = 10;
gfx_y = 10;
gfx_drawstr(hello);
```

`rand()`関数は**0以上引数で指定した値未満のランダムな数**を返します。引数を省略した場合は`1`を与えたのと同じことになります。(0.0～1.0の間の数。ただし1.0は含まない)

ちなみに、この例では文字列がとても長いので、ウィンドウが小さいと描画領域からはみ出てしまいます。自動で改行などはしてくれないので注意しましょう。

## 時間を取得

リアルタイムの信号処理において時間という値はとても大事です。

```
desc:show time

@gfx
gfx_r = rand();
gfx_g = rand();
gfx_b = rand();

gfx_x = 10;
gfx_y = 10;
t = time_precise();
gfx_printf("%f", t);
```

現在の時刻を取得する関数は2種類あります。`time()`と`time_precise()`ですが、前者は**1970年1月1日からの経過秒数を整数で**返し、後者は**システム依存のタイムスタンプを小数で**返します。
絶対的な日時や日付などが重要な場合は`time()`を、ある時点からの相対的な経過時間などが必要な場合は`time_precise()`を使うと良いでしょう。

ちなみにここで初登場した`gfx_printf()`という関数ですが、C言語の`printf`のような感じで画面に文字列を描画します。`gfx_drawstr()`では数値の変数を描画することができません。数値を画面に表示したい場合は`gfx_printf("%f", 数値変数)`のように書きます。

参考: [printf - Wikipedia](https://ja.wikipedia.org/wiki/Printf)


## 条件分岐

```
desc:condition

@gfx
gfx_r = rand();
gfx_g = rand();
gfx_b = rand();

gfx_x = 10;

gfx_y > gfx_h ? gfx_y = 0 : gfx_y += 10;

gfx_drawstr("AAAAAAAAAA");
```

なんと、**JSFXでは`if`文が使えません！**その代わりに条件演算子を使って表します。
この例では`gfx_y`が描画領域の高さよりも大きくなったら`gfx_y`を0に設定し、そうでなければ`gfx_y`の値に10を加算する、という処理をしています。これが`gfx_y > gfx_h ? gfx_y = 0 : gfx_y += 10;`の部分で行われている処理です。

if文で書いたとすると

```
if (gfx_y > gfx_h) {
  gfx_y = 0;
} else {
  gfx_y += 10;
}
```

というような感じになります。

もし◯◯だったら、の中のコードが複数になる場合は`()`でくくって複数のコードを書くこともできます。

```
条件 ? (
  ...
  ...
) : (
  ...
  ...
);
```

という感じで。

## 繰り返し

```
desc:condition

@gfx
gfx_r = rand();
gfx_g = rand();
gfx_b = rand();

gfx_x = 10;
gfx_y = 10;

loop(5, gfx_drawstr("Hello"));

gfx_x = 10;
gfx_y = 30;

while(gfx_x < gfx_w) (
  gfx_drawstr("He");
);
```

一定回数の繰り返し処理を行うには`loop(回数, 処理)`のように書きます。回数には変数を与えることも可能ですが、`loop`に入った時点の値でループ回数が決まり、ループ内で変数を変更しても反映されません。

他のプログラミングに慣れている人は`while(条件) (処理)`という書き方の方がしっくり来るかもしれません。上記の例では`gfx_x < gfx_w`の条件を満たす間、`gfx_drawstr("He")`の処理を繰り返します。

上記の例で`gfx_x`の値をいじっている箇所はどこにもないのに、なんで無限ループにならないんだろう？と思った方もいるかもしれません。
実は`gfx_drawstr`は内部で**描画した文字列の分だけ`gfx_x`,`gfx_y`の値を増やします。**なので、`gfx_drawstr`を繰り返し呼ぶだけで文字が横に並ぶのです。文字列に改行`\n`を含んでいたら、ちゃんと`gfx_y`の値も増えていることがわかります。