TidalCycles 0.8からは、SuperColliderをベースにしたSuperDirtが標準の音響生成エンジンとなりました。そのため、以前は少し面倒だったSuperColliderで独自に作成した楽器(Synth)との連携が簡単になりました。
以下のチュートリアルは、SuperDirtのGithubリポジトリ内にあるチュートリアルをベースにしています。
セットアップ
以降のサンプルを動かすには、TidalCycles 0.8のセットアップが完了している必要があります。セットアップの手順は下記を参照してください。
基本 - 440Hzのサイン波
まず、シンプルなサンプルを作成してみましょう。440Hzのサイン波を生成するシンプルな楽器を定義します。
SynthDef("testsynth", {
arg out;
var sound = SinOsc.ar(440.0);
OffsetOut.ar(out, DirtPan.ar(sound, ~dirt.numChannels, 0.5));
}).add;
これを、Tidalから演奏するには、SuperDirtを起動した状態で以下のように呼び出します。
d1 $ sound "testsynth"
「sound」省略して「s」だけでも大丈夫です。
d1 $ s "testsynth"
Tidal向けに定義されたSynthdefの特徴として、最終出力で、Out.ar(...) ではなく OffsetOut.ar(...) を使用していることに気付きます。これは、Tidalで複雑なリズムを生成する際に、できるだけ正確なタイミングでトリガーされるための工夫です。また、最終出力で、DirtPan.ar(...) を使用しています。これは、Tidalで設定したPanの指定を反映するためのものです。ただし、現状ではまだ反映されていません。また、現在の状態では音がずっと持続してしまい、リズムの切れ目が不明確です。
定位(Pan)と持続時間(Sustain)を設定
もう少し改良して、定位(pan)と音の持続時間(sustain)を設定できるようにしてみましょう。以下のようにSynthdefを修正します。
SynthDef("testsynth", {
arg out, sustain=1, pan;
var env = EnvGen.ar(Env.linen(0.01, 0.98, 0.01, 1,-3), timeScale:sustain, doneAction:2);
var sound = SinOsc.ar(440.0);
OffsetOut.ar(out, DirtPan.ar(sound, ~dirt.numChannels, pan, env));
}).add;
引数に、sustainとpanが追加されました。設定したsustainの値でエンベロープを生成しています。またpanの値をDirtPan.ar()に指定しています。
では、以下のようにTidalから演奏してみましょう。
d1 $ s "testsynth*2" # sustain "0.2" # pan "0 1"
左右交互に、0.2秒の長さで音が鳴るようになりました。
音程を設定
次に音程を指定できるようにしましょう。以下のように修正します。
SynthDef("testsynth", {
arg out, sustain=1, pan, accelerate, freq;
var env = EnvGen.ar(Env.linen(0.01, 0.98, 0.01, 1, -3), timeScale:sustain, doneAction:2);
var sound = SinOsc.ar(freq * Line.kr(1,1+accelerate, sustain));
OffsetOut.ar(out, DirtPan.ar(sound, ~dirt.numChannels, pan, env));
}).add;
Tidalからは、「n (またはnote)」という指定で音程を設定できます。nが9の時、A5(ピアノの中心のラ)の音程になります。例えば以下のようにして音程を指定します。
d1 $ s "testsynth*2" # sustain "0.2" # pan "0 1" # n "9 21"
エンベロープを変更
エンベロープのカーブを調整することで、音の表情が変化します。例えば、よりパーカッシブなエンベロープに変更してみましょう。
SynthDef("testsynth", {
arg out, sustain=1, pan, accelerate, freq;
var env = EnvGen.ar(Env.perc(0.001, 0.999, 1, -4), timeScale:sustain, doneAction:2);
var sound = SinOsc.ar(freq * Line.kr(1,1+accelerate, sustain));
OffsetOut.ar(out, DirtPan.ar(sound, ~dirt.numChannels, pan, env));
}).add;
持続時間を調整します。
d1 $ s "testsynth*2" # sustain "0.5" # pan "0 1" # note "9 21"
応用
あとは、SuperColliderの様々なUGens(ユニットジェネレイター)を使用して複雑な音を生成していくことが可能です。例えば、Roland TR-808風のバスドラム。
SynthDef("super808", {
arg out, speed=1, sustain=1, pan, voice=0, n;
var env, sound, freq;
n = ((n>0)*n) + ((n<1)*3);
freq = (n*10).midicps;
env = EnvGen.ar(Env.linen(0.01, 0, 1, 1, -3), timeScale:sustain, doneAction:2);
sound = LPF.ar(SinOscFB.ar(XLine.ar(100*freq, freq, 0.025/speed), voice), 9000);
OffsetOut.ar(out, DirtPan.ar(sound, ~dirt.numChannels, pan, env))
}).add;
これを、Tidalから演奏します。
d1 $ s "super808*8" # sustain "0.5" # pan "1 0.5 0" # gain "1 0.5 0.75 0 1"
さらに、先程の"testsynth"と同時に鳴らしてみましょう。
d1
$ stack[
s "super808*8" # sustain "0.5" # pan "1 0.5 0" # gain "1 0.5 0.75 0 1",
s "testsynth*8" # sustain "0.25" # pan "0 0.7 0.3 0.5 1" # note "52 40 47" # gain "1 0.5 0.8 1"
]
さらに、juxや、gapなどで操作してみます。
d1
$ jux (iter 8)
$ every 4 (gap 16)
$ stack[
s "super808*8" # sustain "0.5" # pan "1 0.5 0" # gain "1 0.5 0.75 0 1",
s "testsynth*8" # sustain "0.25" # pan "0 0.7 0.3 0.5 1" # note "52 40 47" # gain "1 0.5 0.8 1"
]
徐々に複雑なリズムが生成されるようになりました。様々な工夫でオリジナルな表現が可能となりそうです!