この記事はTouchDesigner Advent Calendar 2020の7日目の記事です。
前日(6日目)の記事は@satoruhigaさんの「感覚的に作るには」です。
TouchDesignerを休日にたしなんでいます @miyukki です。
はじめに
Novation Launchpadとは、Novation社が出しているUSBインターフェースを備えたMIDIインターフェースで、ドラムラックの演奏やサンプラーなど音楽制作で使われることが多いデバイスです。

最近では楽曲制作以外にも、システムの制御やステータスの表示にNovation Launchpadが使われることも増えてきました。おそらく、TouchDesignerユーザーの方であれば1台持っていたりするのではないでしょうか。
ちなみに、私が知っている事例ではInterop Tokyoでネットワーク機器のステータス表示に使っていたりする事例もありました。
ShowNet NOCで面白いのかったの、このMIDIコントローラーでおそらく何かのステータスを表示するやつ #interop2019 pic.twitter.com/7QqctQ9bKu
— みゆっき(@toriimiyukki) June 14, 2019
余談はさておき、このLaunchpadを現場でまじまじと見ていると、マトリックスディスプレイとして使えないかと思い、空いている時間を見つけてTouchDesignerを使って実現しました。toxやコードを貼るだけでも良いのですが、せっかくなので思考プロセスや工程を含めて解説していこうと思います。
Launchpadの基本動作
まず、Launchpadの基本動作についておさらいしておきます。ここでは、TouchDesignerにおけるMIDIデバイスの設定方法などは割愛します。
Launchpadは、64個の何も書かれていないボタンと16個のコントロール用のボタン、計80個のボタンが備わっていますが、このうちコントロール用のボタンに関してはここでは取り扱わないものとします。
MIDI In CHOP を設置してLaunchpadのボタンを押してみると、画像のようになります。

Launchpadでは、ボタン下から1行目が ch1n12〜ch1n19 2行目が ch1n22〜ch1n29... といったチャンネルになるようです。ボタンとチャンネルの対応を分かりやすくすると、以下の画像のようになります。

また、試しに ch1n12〜ch1n19 に対して 適当な値を入れた Constant CHOP を MIDI Out CHOP に接続してみます。MIDIの値は 0-127 のレンジで表現され、MIDI Out CHOPに送られた 0-1 の値は自動的に 0-127 変換されます。

するとデバイスでは以下の画像のように表示されます。

ボタンが押されたチャンネルに対して、値を入れることでボタンを表示させたり、またその色を変えたりできるといったことが分かりました。
Launchpadのボタン色
ここで、勘の良い人であれば、どの値がどの色になっているのかの対応が気になるかと思います。
しかし、私が調べた情報では、0-127の値の中である程度の規則性がある部分とそうでない部分があるそうです。
0-127 の値と色の対応を一覧にまとめた画像がありましたが、確かにこれをコード上でRGBから変換するのは難しいような気がしてきました...

どうにか、RGB->MIDI値の変換ができないか...ということで私の中で出た方法が、表示したい色とLaunchpadのボタン色パターンを比較して一番近いパターンの色を採用するというものです。
楽に考えるのであればPythonを用いるのが良いのですが、8x8のピクセルに対して127通りのループを作るのはパフォーマンスの面であまり気がすすみません。
GLSL Shaderを用いた近似色探索
そこで、GLSL Shader を用いた解決方法を思いつきました。
下図のように 表示したい色とLaunchpadで定義された色をRGBの三次元座標に置いて、それぞれの距離を算出し、一番近いパターンの物を採用するというイメージです。
LaunchpadのRGBカラーについては、運よくGitHubで公開している人がいたので、これを使用することにします。
https://github.com/mohayonao/launch-pad-color
マトリックスディスプレイにする
Launchpadをマトリックスディスプレイとして表示するためには、以下のような流れになると思います。

実際には、以下のようなノード構成になりました。

8x8のためのクロップなどの処理を経て8x8のTOPは、まずGLSLに接続されます。
GLSL Shaderの中身は以下のようになっています。
out vec4 fragColor;
// Launchpadの色パターン
vec4[128] colorMap = vec4[](
vec4( 0, 0, 0, 0), vec4( 80, 80, 80, 1), vec4(160, 160, 160, 2), vec4(240, 240, 240, 3), vec4(255, 145, 133, 4), vec4(255, 35, 10, 5), vec4(230, 31, 9, 6), vec4(204, 28, 8, 7)
// ...略...
);
void main()
{
// 表示したいピクセルの色
vec4 color = texture(sTD2DInputs[0], vUV.st);
vec4 cl;
float d;
float min = distance(0, 1);
int idx = 0;
for (int i = 0; i < colorMap.length(); i++)
{
// 2点間の距離を計算して、一番近いもののインデックス(=MIDI値)を保持する
cl = colorMap[i];
d = distance(color.rgb, cl.rgb/255.0);
if (d < min) {
min = d;
idx = i;
}
}
// GLSLが出力するピクセルのαにMIDI値を代入する
color = vec4(0.0, 0.0, 0.0, colorMap[idx].w/255.0);
fragColor = TDOutputSwizzle(color);
}
GLSLのあとは、αの値(MIDI値)を取り出すための処理と、データをLaunchpadのチャンネルに変換するための処理が続いています。


ちなみに、Rename CHOPは ch1n[1-8][2-9] とすることにより ch1n12, ch1n13, ..., ch1n19, ch1n22, ...となることを知りました。べんり〜
マトリックスディスプレイで遊ぶ
作りたいものが作れた後は、思いっきり遊ぶだけです!
何かしら画像を出してみたり
長い文字をスクロールしてみたり
今日のTouchDesigner Advent Calendarのネタです pic.twitter.com/nZlH9EjCyB
— みゆっき(@toriimiyukki) December 6, 2020
Video Device In TOPとつないでカメラの画像を表示してみたり
— みゆっき (@toriimiyukki) December 6, 2020
あとがき
今回はGLSLを映像処理ではなく、GPUを生かした並列処理を行うオペレーターとして扱ってみました。特にパフォーマンスにシビアなプロジェクトではいかにCPUなど時間のかかる処理をオフロードできるかが重要になってくるかと思います。
また、今回作った8x8のTOPをLaunchpadに表示するやつはtoxとしてGitHub上に公開してますので、是非ご利用ください。興味がある方は、toxの中も覗いてみてください。
https://github.com/miyukki/touchdesigner-launchpad-disp