Blog
SuperCollider自主練 – 基礎編その3
1.6 マルチチャンネル
「1.5 エンクロージャー」のセクションでは、大括弧[]は集合をあらわすという説明だった。ところが、SCの大括弧にはもう一つ意味があって、マルチチャンネルをあらわすという話。
UGen(ユニットジェレータ)の引数にArrayが用いられた場合には、いかなる場合にもマルチチャンネルとみなされる。
{Blip.ar(25, LFNoise0.kr(5, 12, 14), 0.3)}.play // 単一チャンネル
{Blip.ar(25, LFNoise0.kr([5, 10], 12, 14), 0.3)}.play // ステレオ
{Blip.ar(25, LFNoise0.kr([5, 10, 2, 25], 12, 14), 0.3)}.play // 4ch
{Blip.ar(25, LFNoise0.kr([5, 4, 7, 9, 5, 1, 9, 2], 12, 14), 0.3)}.play // 8ch
1.7 ヘルプ
大文字で始まるアイテム(SinOsc, LFSaw, LFNoise, PMOsc, Array, Mix …etc.)や、多くのメッセージ(midi cps, max, loop, random …)はヘルプを持つ。ヘルプを参照したい単語を選択し、cmd-dヘプルを参照できる。
新たなUGen、PMOscを例にヘルプの使いかたを考察。PMOScは位相を変調できるオシレータ、つまりFM合成に利用できる。少ないパラメータで豊かな倍音をもった音響を生成できる。
{PMOsc.ar(440, 550, 7)}.play
PMOscのヘルプを参照すると、その引数の内容が示されている。
ar(carfreq, modfreq, pmindex = 0, modphase = 0, mul = 1, add = 0)
※ キャリア周波数, モジューレタ周波数, インデックス値, モジュレータの位相、全体のスケール、直流成分
既に初期値が設定されている引数(pmindex, mod phase, mil, add)は省略することが可能であるということを意味している。
ネスティングを使用して、より面白いサウンドにしてみる。MouseXとMouseYを使用して、マウスからの入力でモジュレータの周波数とインデックス値を変更できるようにしている。
{PMOsc.ar(440, MouseY.kr(1, 550), MouseX.kr(1, 15))}.play
引数の記述に関するきまりごと。引数はあらかじめ指定された順番で記入していかなくてはならない。ひとつ飛ばしたり順番を変更は基本的にできない。
{PMOsc.ar(100, 500, 10, 0, 0.5)}.play
しかし、キーワードのあとにコロンを付けて引数を指定するという別の方法もある。こうすることで、確実に引数の内容を指定することができ、記述する順番なども関係なくなる。
{PMOsc.ar(carfreq: 100, modfreq: 500, pmindex: 10, mul: 0.5)}.play
{PMOsc.ar(carfreq: 100, mul: 0.5, pmindex: 10, modfreq: 500)}.play //順番変更
ヘルプの活用の実践として、もうひとつ別のサンプルで考察。
{
Blip.ar(
TRand.kr( // frequency or VCO
100, 1000, // range
Impulse.kr(Line.kr(1, 20, 60))), // trigger
TRand.kr( // number of harmonics or VCF
1, 10, // range
Impulse.kr(Line.kr(1, 20, 60))), // trigger
Linen.kr( // mul, or amplitude, VCA
Impulse.kr(Line.kr(1, 20, 60)), // trigger
0, // attack
0.5, // sustain level
1/Line.kr(1, 20, 60)) // trigger
)
}.play
VCO、VCF、VCAという構成の古典的なアナログシンセサイザーを模した減算合成のサンプル。ネストされた要素の内側から外側へと読み解いていく。
3つのLine.krのインスタンスが、それぞれImpulseへ数を送出している。それらが、LinenとTRandをトリガーしている。2つのTRandは、Blipの周波数とハーモニクスの数をランダムに決定している。
ここで、.krと.arの違いについての解説。UGenへのメッセージ.krと.arは、生成される数値のストリームの数を決定している。.arはオーディオレイトの略で、サンプリング周波数と同じ数のストリームを生成(44.1kHzだったら毎秒44100のストリーム)。.krはコントロールレイトの略で、一秒に1ダースほどしか数を生成しない。.krをコントロールに関する部分に使用する計算量の節約になる。
もう1つのImpulseは、Linenをトリガーしている。これはエンベロープと呼ばれるもので、音量の変化の形を作りだしている。Linenの引数は順番に、トリガー、アタックタイム、サステイン(持続)レベル、ディケイ(減衰)タイム。
このサンプルでは、3つのImpulseと2つのTRandと4つのLineといったUGenがそれぞれ独立して使用されてる。これらを連動させていくにはどうすれば良いのか? そのためには、変数を用いなければならない。
1.8 変数
SCでは、小文字のa〜zは、宣言なしに変数として使用できる。SCの変数は、数値、文字列、UGen、関数など様々な値を格納できる。代入にはイコール(=)を用いる。
変数を使用した簡単なサンプル。
( a = 440; b = 3; c = "math operations";
)
これは、以下のプログラムと同じ。
["math operations", 440, 3, 440*3, 440 + 3, 440.pow(3), 440.mod(3)]
次にマウスの位置でSinOscのエンベロープを変化させる例。変数を使用することで、Impuluseのタイミングと減衰時間を一致させている。
( { r = MouseX.kr(1/3, 10); SinOsc.ar(mul: Linen.kr(Impulse.kr(r), 0, 1, 1/r)) }.play )
さらに変数を使用したより複雑なサンプル。「1.7 ヘルプ」で使用した減算合成のサンプルを変数を使用して書き直している。
( p = { // 変数pに関数を代入している r = Line.kr(1, 20, 60); // 演奏タイミング // r = LFTri.kr(1/10) * 3 + 7; t = Impulse.kr(r); // トリガー // t = Dust.kr(r); e = Linen.kr(t, 0, 0.5, 1/r); // エンベロープを生成 f = TRand.kr(1, 10, t); // ランダムな値をトリガーから生成 // f = e + 1 * 4; Blip.ar(f*100, f, e) // 生成された値でBlipを合成 }.play ) p.free; // 演奏の終了
変数を使用することで、すぐに全体の挙動を変更できる。たとえば、r = Line.kr(1, 20, 60); を r = LFTri.kr(1/10) * 3 + 7; に変更するだけで演奏タイミングが変化する。
周波数も、f = TRand.kr(1, 10, t); から f = e + 1 * 4; とするだけですぐに変化する。
ここで、テスト。
( { // carrier and modulator not linked r = Impulse.kr(10); c = TRand.kr(100, 5000, r); m = TRand.kr(100, 5000, r); PMOsc.ar(c, m, 12)*0.3 }.play ) ( { var rate = 4, carrier, modRatio; // declare variables carrier = LFNoise0.kr(rate) * 500 + 700; modRatio = MouseX.kr(1, 2.0); // modulator expressed as ratio, therefore timbre PMOsc.ar(carrier, carrier*modRatio, 12)*0.3 }.play )
問題
- ステレオにせよ
- indexの範囲を、Linke.krを使って1から12まで変化するようにせよ
- それぞれのreceiver.messageのペアの引数を定義せよ
- 演奏スピードを、また別のLine.krを使用して、1から20まで変化するようにせよ
- 全ての引数をキーワード付きにせよ
実際に解いてみた。
ステレオにせよ。
( { // carrier and modulator not linked r = Impulse.kr([10, 10]); // ステレオに c = TRand.kr(100, 5000, r); m = TRand.kr(100, 5000, r); PMOsc.ar(c, m, 12)*0.3 }.play ) ( { var rate = 4, carrier, modRatio; // declare variables carrier = LFNoise0.kr([rate, rate]) * 500 + 700; // ステレオに modRatio = MouseX.kr(1, 2.0); // modulator expressed as ratio, therefore timbre PMOsc.ar(carrier, carrier*modRatio, 12)*0.3 }.play )
indexの範囲を、Linke.krを使って1から12まで変化するようにせよ
( { // carrier and modulator not linked r = Impulse.kr([10, 10]); // ステレオに c = TRand.kr(100, 5000, r); m = TRand.kr(100, 5000, r); i = Line.kr(1, 12, 60); // PMOsc.ar(c, m, i)*0.3 }.play ) ( { var rate = 4, carrier, modRatio, index; // declare variables carrier = LFNoise0.kr([rate, rate]) * 500 + 700; // ステレオに modRatio = MouseX.kr(1, 2.0); index = Line.kr(1, 12, 60); // modulator expressed as ratio, therefore timbre PMOsc.ar(carrier, carrier*modRatio, index)*0.3 }.play )
それぞれのreceiver.messageのペアの引数を定義せよ。※これはちょっと面倒なんで省略
演奏スピードを、また別のLine.krを使用して、1から20まで変化するようにせよ
( { // carrier and modulator not linked t = Line.kr(1, 20, 60); r = Impulse.kr([t, t]); // ステレオに c = TRand.kr(100, 5000, r); m = TRand.kr(100, 5000, r); i = Line.kr(1, 12, 60); // PMOsc.ar(c, m, i)*0.3 }.play ) ( { var rate, carrier, modRatio, index; // declare variables rate = Line.kr(1, 20, 60); carrier = LFNoise0.kr([rate, rate]) * 500 + 700; // ステレオに modRatio = MouseX.kr(1, 2.0); index = Line.kr(1, 12, 60); // modulator expressed as ratio, therefore timbre PMOsc.ar(carrier, carrier*modRatio, index)*0.3 }.play )
全ての引数をキーワード付きにせよ。→ これも省略…