After Effectsのスクリプトの可能性を広げる。 その2

github.com

公開したことでやる気もすこし出てきたので、いくつか更新した。

自分の思想として"ソフトがこちらに合わせろ"というのがあり、既存コマンドに対するフックを容易にするというのもそれに向けての一つの手であったが、更に押し進めてキー入力をフック出来るようにした。

キー入力のフックを上手く活用した一番有名な例はFX Consoleであろうが、こういったランチャーの嚆矢はQuick Menuだと思われる。

AEではScriptsフォルダにあるスクリプトに対してショートカットを割り当てることが可能なので、それを活用している。この方法は言われてみればという感じで、当時甚く感心した。

最大20個までショートカットを設定出来るようなので、やろうと思えば色々出来る。

難点は、管理が面倒だというのと、スクリプトがその都度実行されるため、特に重い処理が含まれる場合に、毎回我慢するか、計算結果をグローバル変数などに逃がしてキャッシュしておく必要がある。Quicke Menuの場合、appオブジェクトに何か生やしているようであった。

キー入力のフックは、このような本質的でない部分による煩わしさから逃れられることが利点であると言える。ただどういった状況下でキー入力がなされたかという情報がないため、例えばレイヤー名変更のために文字入力しているという状況下でも容赦なくイベントは発火する。したがって修飾キーの指定は必須だと思われる。

使い方は至って単純。

(() => {
    Atarabi.keyboard.hook({ code: ']', altKey: true }, ctx => {
        const comp = Atarabi.comp.getMostRecentlyUsedComp();
        if (!comp) {
            return false; // falseを返した場合、(存在すれば)AEのデフォルトの操作が実行される。
        }
        const layers = comp.selectedLayers;
        if (!layers.length) {
            return false;
        }
        try {
            app.beginUndoGroup(']');
            for (const layer of layers) {
                layer.outPoint = comp.time;
            }
        } catch (e) {
            // pass
        } finally {
            app.endUndoGroup();
        }
        return true; // trueを返した場合、AEのデフォルトの操作は実行されない。
    });
})();

Alt+] コマンドは、アウトポイントが現在フレームではなくその次のフレームに設定される。それが煩わしいという人も時たま見るので、上のスクリプトでは現在フレームをそのままアウトポイントに設定するようにしている。

AEのコマンドには、コマンドIDというものが設定されていてそれ経由で実行されるコマンドと、そうでないコマンドとがあり、]Alt+] は後者に属する。なので、コマンドのフックで何とかなる前者だけでなく、キー入力のフックもあれば後者も網羅出来て完璧、とはならず、先程書いた通り、修飾キーのない ] をフックするのは難がある。残念。


同梱物

新たに追加したもの。

Startup

!@script_UI

以前は、スタートアップ時にUI周りのコードを使用しないだろうと思っていたが、割りと使いうると分かったので、リネームして頭に ! をつけることで読み込み順を早めている。

@effect_launcer

www.youtube.com 簡易的なエフェクトランチャー。Ctrl+SpsaceでエフェクトをFuzzy Search出来る。 !@script_UIを用いているので、@script_UIのままの人は頭に!をつけてください。

@hook_]

上では、Alt+]の例を紹介しているが、使用例として一応]も用意してある。

@toggle_mfr

(() => {
    const SECTION = 'Concurrent Frame Rendering';
    const KEY = 'Enable Concurrent Frame Renders';
    Atarabi.keyboard.hook({ code: 'M', altKey: true }, ctx => {
        const now = app.preferences.getPrefAsBool(SECTION, KEY);
        app.preferences.savePrefAsBool(SECTION, KEY, !now);
        writeLn(`MFR: ${!now}`);
        return true;
    });
})();

短いので全部掲載するが、Alt+MでMulti-Frame Renderingのオンオフを切り替える。

@save_to_desktop

(() => {
    Atarabi.keyboard.hook({ code: 'S', altKey: true }, ctx => {
        const comp = Atarabi.comp.getMostRecentlyUsedComp();
        if (comp) {
            const file = new File(`${Folder.desktop.absoluteURI}/${comp.name.replace(/\//g, '-')}_${Date.now()}.png`);
            Atarabi.comp.saveFrameToPng(comp, file);
        }
        return true;
    });
})();

Alt+Sで現在表示されているコンポの画をデスクトップにpngで保存する。


余談。

エフェクトのFuzzy Search周りでは、EditTextのonChangingのコールバック関数にdebounce処理をほどこしている。すべてのイベント発火に対して処理を行うとかなりもたつくからである。

debounce処理の実装にはsetTimeoutとclearTimeoutが使われるが、AEのスクリプトにはないので、app.scheduleTask()、app.cancelTask()を代用することになるが、残念ながら実行したい処理を文字列で指定する必要があるので、グローバルに関数を晒す必要があり非常に使いにくい。

幸いにもCC2015から、app.setTimeout()、app.cancelTimeout()が隠れ機能として追加され(saveFrameToPngと同じ立ち位置?)、これは普通に関数を指定できる。ただ自分が調べた印象だと、app.cancelTimeout()は機能してないように思われる。