【TidalCycles】 SuperDirtにSideChain Compressorを追加する

5 min read読了の目安(約4900字

誰しも人生に一度はサイドチェインの音作りに夢中になる時がくるかと思います。

https://youtu.be/xdO9SzDd3OU
こない・・・?
本記事ではTidalCyclesのサウンドエンジン、SuperDirtをハックして即興演奏にインタラクティブなコンプレッションを導入する方法を紹介します。

ゴール

Tidalでfunctionとしてコンプレッサーを適用し、以下を目指します。

  1. Tidalからthresholdとreleaseをコントロールすること
  2. コントロールもとのBusをd2やd3など自由に指定すること
  3. デフォルトのエフェクトと同様、面倒な準備なしでBootしたら即使えること

実はコンプレッサーの実装自体は、SuperDirtのhacksフォルダの中にいくつかの例
adding-a-compressor.scd filtering-dirt-output.scdなど)があり、これらを参考にすればすぐできてしまいます。

ただ複数のBusからの信号の入出力の管理や、モジュール化に際してのエラーなどTidalとスマートに連携させようとするとちょっと大変でした。コンプの動作確認からモジュール化まで順を追って説明します。
実装にあたり、概要や手順の確認にはmoxusさんの記事が大変参考になりました( https://qiita.com/moxuse@github/items/8da28584e5348463af25 )。
またサイドチェインの詳細な解説はAbletonの記事がアツいです( https://www.ableton.com/ja/blog/sidechain-compression-part-1/ )。
僕の開発環境は(macOS:10.13.6, Tidal:1.7.4, SuperCollider:3.11.1, Editor:Emacs)です。

1.Tidalにパラメーターを追加

SoundのParams.hsに書き込む方法や別のスクリプトに記述して起動時に読み込む方法などがあるようですが、シンプルなのはTidalの起動後に直接実行するやり方です。

table.tidal
let compthr = pF "compthr"
    comprel = pF "comprel"
    compbus = pI "compbus"

コンプレッサーのThresholdであるcompthr、Releaseのcomprel、コントロール信号のBusを指定するcompbusの3種類を設定しました。pFはpattern Float(小数)、pIはpattern Int(整数)を指定しており、なんと"0.25 0.5"のように引数をパターンで記述もできてしまいます。
compbusは、例としてd1にd2からの信号をトリガーにコンプをかけたいとき、

table.tidal
d1 $ s "padlong" # compthr 0.1 # compbus 2 -- d2を指定
d2 $ s "bd hc sd hc"

このように使うことを想定しています。

2.SuperDirtにモジュールを追加

次にSuperDirtにモジュールを追加します。あとで起動時の統合のために別ファイルに記述することになりますが、ひとまず適当な場所にmyEffects.scdとして保存します。

myEffects.scd
(
~dirt.addModule('dirt_compressor', { |dirtEvent|
	dirtEvent.sendSynth('dirt_compressor' ++ ~dirt.numChannels,
		[
			compthr: ~compthr,
			comprel: ~comprel,
			compbus: ~compbus,
			out: ~out
		]
	)
}, { ~compthr.notNil });
~dirt.orderModules(['dirt_compressor']);
)

hacksのcompressorのサンプルではdryBusとeffectBusを使うやり方が書いてありましたが、サイドチェインで他のorbitから信号をとりたいときはOutを使うとうまくいきました。11行目の~compthr.notNilは、tidalでcompthrが記述されるまでエフェクトを適用しないようにするための命令です。

3.エフェクト(Synthdef)の実装

こちらもひとまずmyEffects.scdの下のスペースに記述します

myEffects.scd
(
var numChannels =  ~dirt.numChannels;
SynthDef("dirt_compressor" ++ numChannels, { |out, compbus, compthr=0.1, comprel=0.01, gate = 1|
	var signal = In.ar(out, numChannels);
	var control = In.ar(Select.kr(max(0, compbus-1), ~dirt.orbits.collect { |x| x.dryBus };), 2).sum;
	var thrround = min(compthr, 0.99);
	thrround = max(thrround, 0.0001);
	signal = Compander.ar(signal, control,
		thresh: compthr,
		slopeBelow: 1,
        slopeAbove: 0.1,
        clampTime: 0.1,
        relaxTime: comprel
	);
	signal = signal * EnvGen.kr(Env.asr, gate, doneAction:2);
	ReplaceOut.ar(out, signal);
}, [\ir, \ir]).add;
)

4行目のvar signalにエフェクトを適用する音が入ってきます。その下のvar controlがサイドチェインコンプをコントロールする信号で、Selekt.krを使ってcompbusでBusを指定しています。max(0,compbus-1)としているのはorbitは0はじまり(d1は0、d2は1)なので直感的に扱うため-1してます。また間違えて0や負の数が入った時のためにmaxで最大値が返るようにしています。その下のvar thrroundでも同様に、thresholdの設定ミスで音が爆発しないようmaxminで丸め込みしました。そしてCompanderでコンプレッサーを適用しています。今回Tidalから操作できるようパラメーター化したのはthresholdとreleaseに相当するrelaxTimeの2種類ですが、叩く量を決めるslopeAbove、attackに相当するclampTimeなどもパラメーター化すると面白そうです。ただしスピード感ある即興演奏のために要素を削ぎ落とすことも時として大切になるのではと思います.

ここまでできたらテストしてみましょう。起動手順は

  1. SCDでSuperDirt.start
  2. myEffect.scdで、モジュール追加のブロックとSynthdefのブロックをれぞれ実行
  3. テキストエディタでTidalをBootし、パラメーター追加の行を実行
  4. 演奏する
table.tidal
d1 $ s "padlong" # speed (range 0.5 4 rand) # compthr "0.2" # comprel 0.01 # compbus "2"

d2 $ rarely (ply 8) $ s "md [hh bd?] cp bd" # shape 0.85

d3 $ s "808bd" # gain 1.1 # shape 0.8

d4 $ s "casio*4" # legato 0.1

d1のcompbusを3や4, "2 3 4 3"などにしてみるとわかりやすいでしょう。

4.起動時の読み込み設定

最後に上記の実行手順を簡略化し、SuperDirt.startするだけでコンプにアクセスできるようにします。スタートアップに自作のエフェクトを組み込むためにはファイルロードするのが安全かと思いますが、ちょっとよくわからなかったためここでは直接ソースに書き加える方法をとります(敗北)。しかしこの方法のメリットとして、任意のタイミング(例えばLowpass Filterの前/後など)でコンプを適用することができます。ただファイルロードでもできそうな雰囲気があるのでこれは今後の課題ですね・・・

まずはTidalのパラメーター宣言の3行ですが、これはBootTidal.hsの一番下に書き加えました。こうするとエディタでTidalのBoot時に自動実行されます。

次にmyEffects.scdに書いたモジュールの追加ブロックは、SuperDirtのsynthsフォルダにあるcore-modules.scdに加えました。ここでは一番下にdirt_compressorがくるようにしています(ブロック全体の終了を示す);を超えないように注意していください)。

最後にmyEffects.scdに書いたSynthdefのブロックは、同フォルダにあるcore-synths.scdのSynthdefが並ぶブロックに加えます。ここでmyEffects.scdの時に書いていた一番上のvar numChannels = ~dirt.numChannels;は、core-synth.scdの冒頭で既に宣言されているので削除しましょう。

これでエラーがなければ、SuperDirt.startからのTidal起動ですぐにコンプが使えるようになるはずです。

Thresholdをパターン化して複数レイヤーでのグルーヴを作ったり、テストの時にも触れたcompbusをパターン化してサイクル毎に別のソースからサイドチェインしたりと、Tidalならではの可能性がかなりありそうです。Tidalは慣れたらシンプルなハードウェアのごとく素早く構造を組めるので、サイドチェイン実験もはかどりそうですね。