yoppa.org


immediate bitwave

Blog

TidalCyclesとSuperColliderの連携 – 改訂版

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"
]

徐々に複雑なリズムが生成されるようになりました。様々な工夫でオリジナルな表現が可能となりそうです!