TouchDesignerでOSCコントローラを作ってみる_排他制御スイッチ編

普段TouchDesignerとResolumeを利用してVJをしていて、そこで利用するOSCコントローラを[TouchDesigner](https://www.derivative.ca/)で作っています。
その過程をメモしつつ最終的に出来上がったコントローラーは共有できればなーと思っています。（今回は排他制御スイッチだけです。）

## これを読むと何ができるようになる？
今回は排他制御スイッチ（一つのボタンをONにしたら、ほかの全てのボタンがOFF）が作れます。
あまり大した内容ではないですね…！簡単なのですが微妙に作り辛かった部分があるので書きました。

排他制御スイッチは一つのトグルボタンをONにすると、他のボタンがOFFになります。ResolumeではLayerのClipの切り替えに利用します。
![output.gif](https://qiita-image-store.s3.amazonaws.com/0/65723/463e12b7-0b62-f166-bd74-428bee61656a.gif)
[サンプルtoe](https://drive.google.com/open?id=1Spt60RRPhYgG0jhMaQXaWERCA1Ac2Y9z)

## なんでTouchDesignerでOSCコントローラを作るのか
TouchDesignerと言えばビジュアル制作用途が思い浮かびそうですが、surface goなどの小型winタブレットで利用できる取り回しの良いコントローラーとして利用できないかと考えました。
Touchで作るメリットとしては以下のようなものがあるかなと。

- 無料で利用できる
- 面をguiで作れるので制作スピードが速い
- pythonを記述できるので必要なものは作れる

なので、[touchOSC](https://hexler.net/software/touchosc)や[OpenStageControl](https://osc.ammd.net/)では満足できないけど、[openFrameworks](https://openframeworks.cc/ja/)とかでガリガリ書くのはしんどいって人におすすめです。

特に個人的には、**複数のOSC信号の一括保存と一括呼び出し**をできるようにすることが大きなモチベーションとなっています。
VJでGLSLやTouchDesignerを利用していると、良い感じのビジュアルができてもパラメータが流れてしまい再現が困難なことが多々あります。
そこで、パラメータ群をリアルタイムで読み書きできるコントローラが欲しいなーと思ったことがきっかけです。

## 概略図

![image.png](https://qiita-image-store.s3.amazonaws.com/0/65723/31fc0c18-6d92-725b-425d-db3b5e92e781.png)
OSCは一つにしています。個々のボタンからスクリプトで一つのOSC outを叩いてます。
個々のボタンにOSC outを作ると、送信先のipアドレスを変えないといけなくなった際にだるいので・・・

またアドレスのセッティングは各階層毎にもに記述していて、それぞれが常に親のアドレスを参照しています。なので、OP内では自身をアドレスを追加する追加するだけにしています。

### masterボタンについて
デフォルトのButton COMPにいろいろ手を加えていきます。
また後でReplicatorを利用して複製することを踏まえて制作していきます。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/65723/ef31f378-057d-9314-0d8f-74ff13e76323.png)

#### a:排他制御をする
スイッチがオンになった時に、ここで他のスイッチをすべてOFFにしています。

```
def onOffToOn(panelValue):
	clips = ops('../clip*')
	for op in clips:
		if op != me.parent():
			op.panel.state = 0
	return
```
#### b:OSCのアドレスを作る
TouchDesingerもOSCアドレスも階層構造を持つので、なるべく共通化をしていきます。
ここでは上の階層で ```/hoge``` を作り、親のオペレータの末尾の序数をアドレスに加えています。
Replicatorで複製したときに、各々が個別にアドレスを作るようにしています。

```
op('osc_current_address').text = op('../osc_current_address').text+str(me.parent().digits)
```

必要となるOSCアドレスはツールによりけりですが、この部分を調整することでハンドリングしやすくしています。

#### c. channel名をOSCアドレスにする
Rename CHOPを利用してｂで作ったアドレスをチャンネル名にします。
これも、自分の入力欄だけで記述することもできますが、後から見たときの可読性が上げるためアドレスを外にだしています。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/65723/7507327b-6352-5628-f5c6-a14b7e6ccefd.png)

#### d. OSC out DATを送信する
CHOP Execute DATを利用し、ボタンが押された際に親のOSC outに送信させています。

```
def onOffToOn(channel, sampleIndex, val, prev):
	op('../send_osc_message').sendOSC(str(channel.name), [float(val)])
	return
```
#### ボタンの表示名をいじる
bgOPをいじってReplicatorで複製したときに名称がユニークになるようします。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/65723/d3668f23-d19e-d741-714b-2853dc124e29.png)

親のOP名の後ろの数字部分を表示します。
```parent().digits```
このままだと「１」「１０」のようにボタンの桁数が異なって表示されて残念な感じになります。
そこで、以下を加えることで、「０１」「１０」のようにすべて二桁で表されます。
```'{:0=2}".format(文字列)```


### Replicatorについて
masterボタンをReplicatorを利用して複製します。OPの方は割愛してコールバックの方だけ多少いじりました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/65723/7ef68109-b81f-f5d3-ce8d-d1d5932b4966.png)

#### a: 複製したものを表示非表示を切り替える
masterボタンは表示せず、複製したボタンだけを表示したいので「Parameter>Panel>Display」をOffにします。
このままだと複製したものもDisplayがOffになってしまうので、コールバック内でDisplayするようにします。
```c.par.display = 1```

#### b: 複製したものに対してスクリプトを走らせる
複製したOP内でそれぞれOSCアドレスを自動生成するためにスクリプトを走らせます。
これを走らせないと全てが同じOSCアドレスとなります。
```c.op('calc_osc_current_address').run()```