【AfterEffects】エクステンションとWebSocketでMIDIのイベントを受け取って遊ぶ

PhotoshopをMIDIで操作する端末等を見ていたら面白そうだったので、とりあえずAfterEffectsもMIDIを受け取って制御してみることにします。

> AE前提で書いていますが、基本的にはエクステンションの無いようなのでAE以外のAdobe製品でも使えます。

# 必要なもの
- AfterEffects
- Node.js v6以下 (fsまわりを使用しているMIDIライブラリの関係)
- [nanoKONTROL2](https://www.amazon.co.jp/dp/B004M8UZS8) ¥4000円ぐらい

# 全体図
今回は、Node.jsでMIDIを聴いてイベントを出すwebSocketサーバーを立てて、それをエクステンションで受け取る形で実装します。

![2017-02-25.png](https://qiita-image-store.s3.amazonaws.com/0/85281/b4fda583-d3cc-a19c-c65d-72934a16397b.png)

ソースはこちらで公開しています。
https://github.com/matsurai25/zxp-midi

# server

```js
const app    = require('http').createServer();
const io     = require('socket.io')(app);

app.listen(39393);
io.on('connection', function(socket) {
  // 接続された
  console.log('connection established');
});
```
Node.jsでサーバーを建てます。

```js
const Korg   = require('./korg.js');
const korg   = new Korg();

let timer;
// 全イベントを聴く
korg.on('*', function(event, value) {

  // フェーダーのイベントが送られ過ぎないように、setTimeoutで
  // イベント後一定期間内に再度イベントがった場合はそのイベントを無視するようにしている。
  if (timer !== false) {
      clearTimeout(timer); // setTimeoutを解除
  }

  timer = setTimeout(function() {
    // webSocketでイベントを送信
    io.emit('midi-event', {
      time:moment().format('HH:mm:ss'),
      type:event.split(':')[0],
      channel:event.split(':')[1],
      value:value
    });
  }, 50);
})
```
<img width="412" alt="スクリーンショット 2017-02-26 0.56.19.png" src="https://qiita-image-store.s3.amazonaws.com/0/85281/5bb8e77e-30ad-f716-7520-161679ff489c.png">

MIDIイベントを受け取ってソケットで送ります。
`korg.js`では[korg-nano](https://github.com/davidguttman/korg-nano) を少々変更し、nanoKONTROL2のMIDIマッピングを受け取っています。
今回のフェーダーではイベントが送られすぎてしまってAE側の動作が重くなってしまったので、setTimeoutで短期間にイベントが送られすぎることを制限しています。


# extension
<img width="337" alt="スクリーンショット 2017-02-26 0.57.21.png" src="https://qiita-image-store.s3.amazonaws.com/0/85281/3432fc53-92d0-2cc5-4384-78da2efd121f.png">

AdobeのエクステンションではHTML+jsで色々作ることが出来ます。
つまりはフロントエンド領域の諸々が使えます。

とりあえずなんか良い感じに開発できるセットを作っているので、今回はこちらを使用します
https://github.com/matsurai25/adobe-html5-panel-template

jade/scss等のコンパイル、manifestファイルの設定、zxp署名と書き出しとか出来ます。

## vue.js
今回はどんなイベントが来たかをサクッと見るために `Vue.js` を使用しています。
https://jp.vuejs.org/
日本語ドキュメントがあり、わかりやすいです。

```js
const Vue = require('vue/dist/vue.common.js'); // Vue
const io = require('socket.io-client/dist/socket.io.js');

module.exports = () => {
  const socket = io('http://localhost:39393');
  var app = new Vue({
    el: '#app',
    data: {
      messages: []
    },
    created: function() {
      socket.on('midi-event', (data) => {
        console.log(data);
        this.$data.messages.unshift(data);
      });
    }
  });
};

```
`new Vue()`に色々渡してリストレンダリングとイベント設定をします。
`created`で指定した関数がマウント完了時に実行されます。
`socket.on(key, function)`で、指定のkeyのソケットが送られてきた時の処理を行います。
これは受け取ったjsonをとりあえず`unshift`で`message`配列に入れていっています。DOM側は自動的に更新されます。

```jade
    #app
      .message(v-for='message in messages')
        .time
          | {{ message.time }}
        .type
          | {{ message.type }}
        .channel
          | {{ message.channel }}
        .value
          | {{ message.value }}
```
DOM側ではこんな感じに指定しておきます。
スタイルはscssで良い感じにあてます。

# エクスプレッション
<img width="875" alt="スクリーンショット 2017-02-26 1.07.28.png" src="https://qiita-image-store.s3.amazonaws.com/0/85281/f5cac1be-af8b-ca10-94c2-3601e46869d7.png">
`_slider`というコンポジションを作成し、`layer`というレイヤーを作成、そこにスライダー制御をいっぱい付けて、それをすべてのレイヤーで参照するようにします。


```js
var s = comp('_slider').layer('slider').effect('slider:'+n)(1);
```
とかで値を取得できます。

<img width="621" alt="スクリーンショット 2017-02-26 1.06.46.png" src="https://qiita-image-store.s3.amazonaws.com/0/85281/55b26c1d-1853-7bf4-9f32-8f0ebaa8fa45.png">
とりあえず良い感じに見れるようにしたもの。

<img width="1215" alt="スクリーンショット 2017-02-26 1.10.53.png" src="https://qiita-image-store.s3.amazonaws.com/0/85281/9f536fd6-c1e1-3f0a-f6e1-98cab6da89bf.png">

```js
var k = comp('_slider').layer('slider').effect('slider:1')(1);
effect("色相/彩度")(7)+index*20 + 5*k;
```

適当に●をいっぱい置いて、色相エフェクトをフェーダーで制御してみます。

aepをこちらに置いておきますので、ご自由にご利用下さい(CC2014)
https://github.com/matsurai25/zxp-midi/blob/master/AE_MIDI_SLIDER_sample.aep


# 完成
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">意味がわからないのですが、AfterEffectsをMIDIフェーダーで操作するエクステンションを書きました。たのしい。 <a href="https://t.co/HFlnHrymOA">pic.twitter.com/HFlnHrymOA</a></p>&mdash; matsurai (@matsurai25) <a href="https://twitter.com/matsurai25/status/835358385180565504">2017年2月25日</a></blockquote>

# 感想
- エクステンションまわりの記事少ない・・・。
- manifest.xmlに不要なjsx読み込みが書いてあるとCSInterfaceが動かないという問題にしばらく悩まされました。
- AE側でリアルタイムに値を制御したかったのですが、いちいちスクリプトを噛ませて実行するとやはり速度に難ありという印象。
- 選択されているプロパティの数値を上げるとかのショートカットキーがあれば、そちらを再現してあげればよさそう。
- スクリプトのショートカット割りあてが結構面倒な印象があるので、これと[nanoPAD](https://www.amazon.co.jp/dp/B004M8YPKM)を使って良い感じにすれば、結構業務効率向上しそうですね。
- スクリプトはグローバルを共有しているので、EventEmitterとかをglobalに入れてsocketを通知してあげれば、他のスクリプトでも購読できて良さそう(by [かれおばなさん](https://twitter.com/kareobana/status/835381098871844864))

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">webSocketサーバーをネット上に立てて、何かのキーでリクエストしたらそこから配信されたスクリプトを手元で実行させる、みたいなこともできる。スクリプトファイルが常に使い捨てのコードになって、手元で管理しなくてもよくなる。これ面白そうだし暇な時に試そう</p>&mdash; matsurai (@matsurai25) <a href="https://twitter.com/matsurai25/status/835360895647637504">2017年2月25日</a></blockquote>

以上です。