TidalCyclesとToudhDesignerをつなげる

## はじめに
しゃんぺんです。こんにちは。

趣味でTouchDesignerを楽しんでます。
今年は素敵なAudio Visual Liveを沢山浴びる機会があり、勢いで「Audioもやりたい！」となり、TidalCyclesに手を出し始めました。

この記事ではTidalCyclesとTouchDesignerを連携させる方法について紹介します。

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">アドベントカレンダーの記事に貼るやつ😉<a href="https://twitter.com/hashtag/tidalcycles?src=hash&amp;ref_src=twsrc%5Etfw">#tidalcycles</a> <a href="https://twitter.com/hashtag/touchdesigner?src=hash&amp;ref_src=twsrc%5Etfw">#touchdesigner</a> <a href="https://t.co/oVBDZSdTCa">pic.twitter.com/oVBDZSdTCa</a></p>&mdash; Shampagne (@Shampedelica) <a href="https://twitter.com/Shampedelica/status/1471838421425725441?ref_src=twsrc%5Etfw">December 17, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

サンプルファイルはこちら。
https://github.com/thinpedelica/tctd


### 補助テキスト
本稿ではTidalCyclesについてはほとんど説明しません(できません)。
別途以下を参考にしてTidalCyclesを始めてください！！

- 演奏するプログラミング、ライブコーディングの思想と実践  
https://www.amazon.co.jp/dp/4802511043
- moistpeaceさんのブログ  
https://moistpeace.com/
- インストールハマりログ  
https://shampagne.hatenablog.com/entry/20211023/1635000341


## 音をTouchDesignerに入力する
TidalCycles/SuperColliderが出力する音をTDに引き回すために、「VoiceMeeter Banana」という仮想ミキサーを利用しました。
選択理由は、「特殊な機材が不要＆無料」です。ただ、申し訳ないのですがWindows専用ツールでした。

VoiceMeeter Bananaの詳細は[こちら](link https://ordinarysound.com/voicemeeter-banana/#index_id0)が詳しいです。
VoiceMeeter Bananaをインストールすると、仮想オーディオデバイスとして認識されるようになります。
これにより、TidalCycles(SuperCollider) -> VoiceMeeter Banana -> TouchDesignerと入力できるようになります。


手順は以下のとおりです。

- VoiceMeeter Bananaをインストールする  
https://vb-audio.com/Voicemeeter/banana.htm
- VoiceMeeter Bananaを起動する
- SuperColliderが認識しているデバイス一覧を取得する  

以下のコマンドをSuperColliderで実行すると、一覧が取得できます。

```
ServerOptions.devices;
```

```
-> [ MME : Microsoft Sound Mapper - Input, 
     MME : VoiceMeeter Aux Output (VB-Audi, 
     MME : マイク配列 (Realtek(R) Audio), 
     MME : ライン (NewTek NDI Audio), 
     MME : VoiceMeeter Output (VB-Audio Vo, 
     MME : Microsoft Sound Mapper - Output, 
     MME : スピーカー (Realtek(R) Audio), 
     MME : VoiceMeeter Aux Input (VB-Audio, 
     MME : VoiceMeeter Input (VB-Audio Voi, 
     Windows DirectSound : プライマリ サウンド キャプチャ ドライバー, 
     Windows DirectSound : VoiceMeeter Aux Output (VB-Audio VoiceMeeter AUX VA...etc...
```

- SuperColliderの音の出力先にVoiceMeeter Bananaを指定する  
上記の一覧から、「VoiceMeeter Input」もしくは「VoiceMeeter Aux Input」を指定して、以下のようにコマンドを実行します。

```
o = Server.default.options;
o.outDevice_("MME : VoiceMeeter Aux Input (VB-Audio");
```

![audio_in_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/880453/58f9949b-ce28-940e-a1a6-029ad1f0304b.png)


- VoiceMeeter Bananaの出力先を設定する  
![audio_in_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/880453/75a1359d-0a25-b550-a610-931a0453757a.png)


画像のオレンジ色の枠で囲んだ部分が出力先のOn/Offの指定になります。
A1が物理出力、B1/B2が仮想出力(VoiceMeeter Output, VoiceMeeter AUX Output)です。
実際に音を鳴らすには、A1の物理出力先を赤枠の部分から選択する必要があります。

B1/B2はどちらか片方だけがOnであればよいです。

- TDのAudioDevice inでVoiceMeeter Bananaから音の信号を取得する  
ここまでのステップが出来ていれば、TDのAudioDeviceIn ChopでVoiceMeeter OutputもしくはVoiceMeeter AUX Outputを選択すると、音の信号を得ることができます。
![audio_in_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/880453/4de18251-843e-3e37-0814-e84a2ac75faf.png)


これでAudioVisualやり放題です。




## コーディング画面をTouchDesignerに入力する
TidalCyclesで演奏するなら、当然コーディング画面もスクリーンに映したくなりますよね。
なんならTDに入力して、映像と重ねたくなりますよね。

ということで、OBSでエディタをキャプチャしてNDIでTDに引き回しました。
手順は[こちら](https://ino2408m-pinv.hatenadiary.jp/entry/2021/05/14/033556)を参考にしました。


いちおう私が実施した手順を記載しておきます。

- OBSStudioをインストールする  
  https://obsproject.com/ja
- NDIプラグインをインストールする  
  https://github.com/Palakis/obs-ndi/releases
- 「ソース -> 追加 -> ウィンドウキャプチャ」で、エディタを指定する
- 「ツール -> NDI Output settings」で、適当にNDI出力に名前をあてる
- 「右クリック -> 変換 -> 画面に合わせて置く」を選択
- 「右クリック -> フィルタ -> クロップ/パッド」で上下左右の余計な部分をカット
- TDのNDI In Chopでエディタ画面を入力する
- Composit Chopの「screen」でよしなにする
![editor_in_7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/880453/e5941e17-7fb1-de2b-219a-194aeb484b7a.png)



## OSCをTouchDesignerに入力する
TidalCyclesとSuperColliderはOSCで通信しているのですが、SuperColliderに一筆加えてやると、TidalCyclesからのOSCメッセージを外部に中継してくれるようになります。
TidalCyclesのコマンドで映像の制御が出来たら便利という噂(from moistpeaceさん)、、ということで、やってみました。

### OSCメッセージの構造
まず、TidalCyclesが出力しているOSCメッセージの構造を解析しました。

##### TidalCyclesのコマンド構文  

```
d9
    $ sound "yeah yeah yeah yeah" # gain 0.0
    # note 1
    # resonance 0.5
    # speed 0.3
    # distort 0.8

```

##### 出力されるOSCメッセージ
```
/dirt/play
_id_
9
cps
0.5625
cycle
26.0
delta
1.7777
distort
0.8
gain
0.0
note
1.0
orbit
8
resonance
0.5
s
yeah
speed
0.3
```

#### OSCアドレス
- /dirt/play  
TidalCyclesのバージョンの差分なのか、ここが"/play2"となっていることもあるようです。

#### OSCデータ
データは、パラメータ名と数値のペアになっているようでした。

- トラック：\_id_, orbit
TidalCyclesはd1,d2,d3,,,と複数のトラックがあり、複数のトラックを並行して鳴らすことができます。
このトラック数、諸説あるのですが手元で確認した限りd1～d16まで利用可能でした(過去はd9が最大だった模様)。
OSCメッセージを調べた限り、どのトラックのメッセージかは、\_id_とorbitに反映されているようです。
\_id_は1オリジンの値(d9なら9)、orbitは0オリジンの値(d9なら8)といった具合でした。

- 音のタイミング関連：cps(cycle per second),cycle,delta
映像の制御には使わない情報だと思うので説明は割愛します。

- 音の種類：s
sはsoundの略で、上記例では"yeah"という名称のsoundを使用しています。

- エフェクト：note, resonance, speed, distort, gain
エフェクト名と数値の順で送信されます。


### OSCメッセージの送信タイミング
TidalCyclesからSuperColliderへのOSCメッセージは、音を鳴らすタイミングで毎回送信されていました。
例えば、"bd bd bd bd"といった「バスドラを1cycleで4回」というコマンドの場合、1cycleで4回のOSCメッセージが送信されます。


### OSCメッセージを作るうえでの注意点
- 文法エラーの場合、OSCメッセージは送信されない  
実在するエフェクトやパラメタ値を使用してメッセージを構成する必要があります。


### Myコマンド構成
上記の特性を踏まえ、以下のようなメッセージで映像制御することにしました。

```
d9
    $ sound "yeah yeah yeah yeah" # gain 0.0
    # note 1
    # resonance 0.5
    # speed 0.3
    # distort 0.8
```

- sound
なんでもよかったのですが、ごきげんな雰囲気にしたくて"yeah"を選びました。

- gain
実際に音が鳴っては困るので、gain 0を指定することで音量0にしました。

- note
映像の切り替えに使用しました

- resonance, speed, distort
映像に入力するパラメータとして使用しました。
選んだエフェクト名はなんとなくです。


### SuperColliderの設定
以下のコマンドをstartup.scdに書くか、IDE上で記述し実行します。

```
(
var addr = NetAddr.new("127.0.0.1", 7000);
OSCFunc({
arg msg;
if(msg[2] == '9', {addr.sendMsg("/tidal", *msg)}, {});
},'/dirt/play').fix;
)
```

OSCFuncは、指定のOSCアドレス(ここでは/dirt/play)を受け取ったときに実行するコールバック関数を登録するもののようです。
上記では、受信したOSCメッセージ内容を'/tidal'というOSCアドレスとして丸ごと「"127.0.0.1":7000」に送っています。
msg[2] == '9'は\_id_の値を見ており、負荷軽減のためにd9の場合のみTDに送信するようにしました。

### OSCメッセージのパース
OscIn Datでパースしました。
![osc_in_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/880453/30c51d62-8fb5-30d7-564a-6795a885f132.png)


argsを全チェックして、TABLE_INDEX dict内のキー名と一致したら値を抜く、みたいなのです。

```
TABLE_INDEX = {"note" : 0, "resonance" : 1, "speed" : 2, "distort" : 3}
COL_VAL = 1

def onReceiveOSC(dat, rowIndex, message, bytes, timeStamp, address, args, peer):
	table_params =  op("table_params")
	args_index = 0
	args_index_max = len(args)
	while args_index < args_index_max:
		if args[args_index] in TABLE_INDEX:
			target_raw = TABLE_INDEX[args[args_index]]
			target_val = args[args_index + 1]
			table_params[target_raw, COL_VAL] = target_val
			args_index += 2
		else:
			args_index += 1

	return
```

これでTC->TDできますね。

## おわりに
TouchDesignerのTipsが全くない記事になってしまいましたが、音楽も鳴らせるようになったらTouchDesignerをより一層楽しむことができる、と思っています。
また、TidalCyclesを使っている方たちにも、ぜひTouchDesignerとつなげて楽しんでもらいたいです。