TouchDesignerでPythonを使ってみたい

普段はWebアプリ開発、データ解析みたいなことをPythonでやっているのですが、
ひょんなことからDMX, ArtNet, Arduino, Neopixel等を触る機会がありまして、
その延長上で「TouchDesingner」というものを知りました。

https://r-dimension.xsrv.jp/classes_j/about-touchdesigner/

プログラミングとPCだけで渡り歩く「情報」の世界から、我々の肉体が存在する「物理世界」への"バックドア"を見つけたような気がして面白そうだったので、
まったくのTouchDesigner初心者ですが、TDでPythonを使ってみるにはどうすればいいのかをちょっとだけ調べてだいぶ雑にまとめてみます。


# TouchDesignerのインストール

お使いの環境に合わせて↓からインストールすればいいのだと思います！
非商用の個人利用なら無料らしいです。感謝。

https://derivative.ca/download

# TouchDesignerの使い方

TouchDesigner自体の使い方の記事はいっぱいあるので割愛します！
↓とかいいのかも？

https://qiita.com/sanokazuya0306/items/75eeb7009bf4eb5678bc

# TouchDesignerでPythonを使う

ここからが本題。
いかにしてPythonを使えばよいのやら。
ということで調べて見ると、TouchDesignerを作っている会社であるDerivativeさんが丁寧に説明してくれています。
（そもそもTDのドキュメント全般をWikiで書いてくれているっぽいですね。）

https://docs.derivative.ca/Introduction_to_Python_Tutorial

今回は、このチュートリアルをちょっとだけ紹介していきたいと思います。
とりあえず、TouchDesignerを開いて、最初に表示されている箱をCtrl+A→Deleteで削除しておきましょう。

## 文字列の表示

一応チュートリアルということなので、まずText TOPにPythonの値を表示してみます。
（TOPというのは"Texture Operator"のことで画像をこねくり回すことができる箱です。）

Tabキーを開いて、「text」と入力し、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1182847/ecc5840c-e916-c3cd-6176-5f039f7e41b0.png)
Text TOPを見つけて配置します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1182847/60840811-bcb2-264c-0d59-b273c9ff4386.png)
こんな感じでデフォルトでは"derivative"が表示されます。
右上にあるのは、「パラメータウィンドウ」といって、箱が持っているパラメータの一覧です。（表示されていない場合はキーボードの「p」を押すと表示/非表示を切り替えれます。）

パラメータウィンドウの「Text」タブの「Text」パラメータをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1182847/e3d4708c-bc42-ba75-4d06-cadef9111c9a.png)
すると、パラメータが下部に展開されるので、
青い四角形（カーソルを当てると「expression」と表示される）をクリックして、右側の入力欄にPythonの文字列みたく

```
'Hello Python'
```
と入力すると、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1182847/e8c6b55e-e938-0566-d718-50d24f5140e0.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1182847/65da884b-441d-cb38-e1e7-95bb9065f196.png)

おー、'Hello Python'という文字列が画像として表示されました。
このままだと、文字列を表示するのと何も変わらないので、足し算にしてみます。

```
'Hello ' + 'Python'
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1182847/57402fd5-60a2-330a-a7b8-b030fd03288a.png)

おー、ちゃんと文字列の結合がされている。
どうせなら、もう少しPythonっぽいことがしたいので、関数を記述してみます。

Textパラメータを

```
dir()
```
として（dir関数はスコープにある名前のリストを表示してくれます）、Word WrapパラメータをOnにすると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1182847/1e6c06a8-ec27-56be-c384-24ddf355a242.png)

おー！呼び出せそうな変数、関数が表示されている！

こんな感じで、パラメータウィンドウに1行だけ（？）Pythonを記述できるらしいです。

## TouchDesignerのオブジェクト

TouchDesignerでは、箱（ノード、オペレータ）を繋げていって処理を行うわけですが、
Pythonを記述する場合には、自分自身の持っているパラメータや、他のノードのパラメータを取得したい場合があります。
そのためには、`me`クラスとか`op`クラスとか`parent`クラスを使います。

* `me`クラス：自分自身
* `op`クラス：オペレータ（箱）を取得できるクラス
* `parent`クラス：親オペレータ

まだ勉強中なので詳しく説明できないのですが、とりあえず箱（オペレータ）のパラメータをPythonで取得したい場合は、今紹介した人たちを使えば良いらしいです。
オペレータの名前は、

```
オペレータ.name
```
で取得できます。

また、箱（オペレータ）の持っているパラメータには`par`クラスで参照できます。

* `par`クラス：パラメータ達にアクセスできるクラス

たとえば、 

```
me.par.text # 自分自身のTextパラメータ
op('text2').par.fontsizex # text2オペレータのFont Size X パラメータ
```
みたいな感じです。

パラメータウィンドウに表示されているパラメータのparクラスにおけるプロパティ名を知りたい場合は、
ウィンドウで各パラメータをクリックすると下部に展開される入力欄の左側を見れば確認できます。
たとえば、下の画像の「Font Size Xパラメータ」のプロパティ名は「fontsizex」です。
![スクリーンショット 2021-12-16 173040.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1182847/7638a9ce-b393-be5a-d883-2b4790b942aa.png)

TouchDesignerには音声とか信号を扱えるCHOP(CHannel OPerators)もあります。
CHOPは「チャンネル」といって、Pythonのリストのように複数の値を持っている場合が多いです。
実際に、CHOPを作ってみます。
Tabキーを押して、「Constant」と入力して、Constant CHOPを探してEnterを押すと、画面にConstant CHOPが追加されます。
パラメータウィンドウのConstantタブで下部の「＋」ボタンを押すとチャンネルを増やすことができます。
さらに、valueを適当にいじると下の画像みたいになります。
（`chan1`と`chan2`の2つのチャンネルがあります。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1182847/557e6f64-f366-027a-5b97-dbef6ade8a9f.png)
このConstant CHOPのチャンネルを取得したい場合は、

```
op('constant1')['chan1'] # constant1オペレータの'chan1'チャンネル
```
みたいに、辞書型のイメージでチャンネル名を指定すれば値を取得できます。

ここで、パラメータとかチャンネルを取得するときに注意なのが、関数の引数とかに指定しようとするとエラーを吐く場合がある、ということです。
これは、実際に取得されるのが値そのものではなくTouchDesignerのクラスだったりするのが理由です。
値を取得したい場合には、

```
op('constant1')['chan1'].eval()
```
みたく、`eval`メソッドを使うと良いらしいです。
`round`関数で丸めるときなどは注意しましょう。


## シェルを開いて実行したい-Textportについて-

ここまでは、Text TOPのパラメータにPythonっぽいことを書く、というThe初心者って感じの方法でPythonを使ったわけですが、
当然ながらもっとスマートな方法があります。
それが、Textportを使う方法です。
TouchDesignerの左上の「Pane Layout」から縦半分になっているものをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1182847/ef8feb90-02ba-928c-91ef-5b10fea0b5b2.png)
すると、画面が2分割されるはずです。
さらに、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1182847/c73cb584-6e59-e47a-3959-e7d4e62a29a9.png)
右側の画面の上部にある下三角をクリックして「Textport and DATs」をクリックすると、、、

おなじみPythonシェルが開きます！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1182847/89ee701f-772f-05e9-eb5c-e3880ea28a62.png)

では早速Textportから、TouchDesignerのオブジェクトを触ってみましょう。
さっき設置したconstant1 CHOPを取得してみます。

```
python >>> r = op('constant1')
python >>> debug(r) # debug関数を使うと呼び出し元がわかるから便利らしい！
# 実行結果 -> None (Debug - DAT:<Textport> fn:<module> line:1)
```
となり、取得できていない！
これは、Textportがある場所からはconstant1 CHOPを直接呼び出せないからです。（スコープの問題みたいな？）

呼び出すためには、ちゃんとパスを指定しなければならないのですが、
どうすればいいのかというと、「ドラッグアンドドロップ」！

```
r = op('# ここにドラッグアンドドロップ
```
自動でパスを入力してくれるので、そのままパスの文字列を閉じればOK！

実際にやってみると
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1182847/91c43e31-6549-239f-fde0-bc968e0b5fc4.png)
こんな感じになります。
なるほど、constant1はproject1の中にあったということがわかりますね。

## スクリプトを書いて実行したい-DATについて-

これまでは「Pythonをオペレータのパラメータで使う」、「Textportで対話型で使う」という方法をとってきました。
しかし、実際はあらかじめ書かれたコードを実行したいですよね。
（そうしないとPythonを書く意味が、、、）

ということで、DAT（Data Operator）を使ってみましょう。（DATは文字列、スクリプト、XMLなどのテキストデータを扱うらしいです。）
DATを使うことで、Pythonスクリプトを実行することができるようになるので、TouchDesignerでプログラミングしてる感がでてきます！（たぶん。）

DATでPythonを扱うには、いくつかの方法があるらしいです。

1. Callbacks, Execute DATs  
    TouchDesignerでの種々の変化に合わせて実行される. onValueChangeとか。
2. Script OPs  
    スクリプトを使ってCHOP,DAT,SOP,TOPを作る
3. Extenstions  
    Pythonを使ってメソッドとか変数を拡張できるらしい
4. Module  
    モジュールとして扱える

大まかにはこの4つの方法があるらしいです。
インタラクティブな表現っていったら、やっぱり1のCallbackとかが入りやすいのかなって思ったりします。
（もっとちゃんとやると、2,3,4をゴリゴリに書いたりするのかな、、、）

そろそろ疲れてきたので、1のCallbackだけを紹介して終わりたいと思います。
チュートリアルにあるように、TouchDesignerの時間経過に合わせて背景色が変化するText TOPを作ってみましょう。
まずは、TouhDesignerの時間経過をText TOPに表示することにします。
Text TOPのTextパラメータに

```
absTime.frame
```
と入力しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1182847/e4947846-edfd-3edc-2cfe-3b57baba5213.png)
すると目まぐるしく数字が変わる画像が表示されると思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1182847/145b32f6-b293-03b1-cd04-c2bd722e9424.png)

次に、パラメータの変化に応じてメソッドの実行を指定できるParameter Execute DATを配置します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1182847/4206aeba-95bb-0a6e-224b-1c9558188377.png)
こんな感じになるはずです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1182847/c1763aa6-d4c2-e4c2-97fd-21bcd7449c73.png)

続いて、Parameter Execute DATのパラメータウィンドウのOPsパラメータにText TOPをドラッグアンドドロップします。（ドラッグ中は矢印みたいなのがでるはずです。）
すると、Text TOPからParameter Execute DATに向かってなんか流れます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1182847/9a326cb5-b05c-fc0e-5672-698f129d1ffe.png)
さらに、Parametersパラメーラ（わかりづらいですね笑）に、Text TOPの指定したパラメータ名を入力します。この場合は、textパラメータです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1182847/b0370f6f-f030-a021-d059-218cbb6bed37.png)
あとは、Parameter Execute DATの中身を書き換えれば、Text Topのtextパラメータに応じた処理ができます！

ひとまず、`onValueChange`メソッドをパラメータをTextportに表示するように変更しましょう。
テキストを変更するには、箱の右下の「＋」マークみたいなのを押した後、箱の中身をクリックすればできます。

```
def onValueChange(par, prev):
	debug(par)
	return
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1182847/6e813ab8-a038-1891-8ce9-2e52a4dd84c2.png)

あとは、「Altキー」+「T」でTextportを開いてメソッドが実行されていることを確認しましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1182847/055d7b55-2e43-8416-04b9-f28cf385098a.png)
実行されているのが確認できればOKです。

目的は背景色を変えることなので、onValueChangeの処理を書き換えましょう。

```
import random

def onValueChange(par, prev):
	op('text1').par.bgcolorr = random.random()
	op('text1').par.bgcolorg = random.random()
	op('text1').par.bgcolorb = random.random()
	return
```

これで行けるかと思いきや、変わっていません。
そこで、Text TOPのパラメータウィンドウのColorタブを開いてみると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1182847/63a2ef64-e5a2-1e57-5273-eda938424a74.png)
たしかにBackground Colorは変わっています。
ただ、Background Alphaが0のままで透明になっていました。
そこで、Background Alphaを1にしてみます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1182847/22482ec1-cf37-0b30-494f-88e0e7a8ddac.png)
おー。ようやく目的の背景色が変わるText TOPを作ることができました。

# まとめ
この記事では、ただチュートリアルをものすごく雑に訳しただけになってしまったのですが、本当はもっと面白いことがやってみたいです。
今後も少しずつ勉強していきたいと思います。
TouchDesigner自体のチュートリアルはいっぱいあるし、Pythonどう使うかちゃんと書いてある記事もあると思うので、ぜひみなさんもエンタメPythonを楽しんでみてください！
（記事の間違いなどあればご教示願います！！！！）