クラス書いてみる

一連の処理をクラスという形にまとめておくと、使い回しが効いて便利らしいです。
PsyColliderの起動時に、猛烈な勢いで文字が流れてますが、あの中に「compiling class library..」「compile done」といった文言があって、どうもクラスというのは起動時に読み込まれるもののようです。
ということは、これはもう定番だろう、という処理があるとすれば、クラスにしておけば起動時に読み込まれるので毎回書く必要はないし、実に便利なのですねー
という程度の理解なのですが、ではクラスというものを書いてみようと思います。

書く

新しいウィンドウにこう書きます。

Hoge {			// クラス名
	*hoge {			// クラスメソッド名
		|in|		// 引数
		^(in*2)		// in*2を返す
	}
}

まずクラス名を書き、その中のメソッドの部分に、処理の内容を書きつけます。
クラス直下のメソッドであるクラスメソッド名には*をつけるようです。
処理の内容としては、引数で値や音をもらって、ごにょごにょしたのちに、^で戻してもらいます。
骨組みとしてはこんだけです。

コンパイル

さてPsyColliderでは、これ書いただけだとクラスが使えません。なぬー
書いたらこれを拡張子scのファイルとして、C:\Program Files\PsyCollider\SCClassLibraryの中に保存します。
自分はここにさらにフォルダをひとつ作って、その中に置いてます。意味があるかはわかりません。
保存ができたらAlt+Kします。するとクラスライブラリのコンパイルががっちょんがっちょんと始まります。
ほとんど再起動みたいな感じですが、再起動する手もありますよ。
(当方の環境ですと、何度もAlt+Kしてるとアプリケーションが落ちたりするのですが、Ctrl+Alt+Delでタスクマネージャを見ると、java.exeが顔面蒼白で固まってることなどありますので、この場合一旦落としてやる)
コンパイルが済んだら、起動時の状態になってますので、必要に応じて再度localhost serverを起動します。
このように、非常に回りくどいのですが仕方がない。

実行

いよいよHogeクラスの実行です。正しくはHogeクラスのhogeメソッド実行?なんやわかりませんが

Hoge.hoge(2)

4と出たらば成功です。おめでとうございます。
単に来たものを2倍するだけの変哲もないものですが、わしにもクラスが書けた。
要するに「*2」だけが内容ですので、当然音もつっこめます。

{ Hoge.hoge(SinOsc.ar(1000)) }.play;

るせー。

メソッド

同じクラスにメソッドを複数書けます。というかいろんなメソッドをひとつのクラスにまとめられると言う方がいいのか?
クラスと言えばSinOscなんかも同様に、arとkrというふたつのメソッドがあるということのようです(Alt+Jで見られます)。

Hoge {
	*hoge { |in|
		^(in*2)
	}

	*moge { |in|
		^(in/2)
	}
}

Hogeクラスにはhogeメソッドとmogeメソッドがあるのです。と書いてある。

Hoge.moge(3)

1.5です。おめ。

クラス変数

ていうかもう少し意味のありそうなものを書こうかと思います。とはいえ何を書いたものか…
同じscファイルでいいので、以下を書きます。

Ghpr {
	classvar a, x;		// クラス変数

	*new { |aa, xx|
		a = aa;
		x = xx;
		^x
	}

	*next {
		x = (a*x)*(1-x);
		^x
	}
 }

これはまた何か?ところで、クラスの中で変数を使い回すにはクラス変数と書かねばならぬようです。
ではやってみる。

Ghpr.new(3.7, 0.5)

ここでクラス変数に代入。0.5と言われましたが、これが初期値になります。

Ghpr.next()

0.925です。
もういちど押すと、0.2566875です。nextするたびに計算を繰り返す仕組みです。
では100回まとめてやってみる。

100.do{ Ghpr.next().postln };

どざーっと数字が出たと思いますが、どうにもデタラメっぽい並びです。
randなどないのに、どうしたことでしょうね?
説明は省きます。

音クラス

音クラスという言い方はないと思いますが、クラスにUGen入れたいと思います。
やり方としては、箇条書きでテストしてみて、クラスに書き直す感じ。

Moge {
	*ar { |freq|
		var o;
		o = SinOsc.ar(freq);
		o = Pan2.ar(o, 0);
		^o
	}
}

箇条書きで書いてるのとほとんど変わらんと思います。

{ Moge.ar(200) }.play;

ちゃんと鳴る。
ていうかもう少し意味のありそうなものを書こうかと思います。

Keri {
	*ar {
		var e, o;
		e = EnvGen.ar(Env.perc(0, 0.2)); 
		o = SinOsc.ar(e**10*1000+60, 0, e);
		o = Pan2.ar(o, 0);
		^o
	}
}

こないだのキックです。

{ Keri.ar }.play;

これだけでキックが鳴る。大変に便利。
UGenの入れ子みたいなこともやってみます。

Pong {
	*ar{ |freq, rate=1, amp=1|
		var a, b, c, o;
		a = Impulse.ar(rate);
		b = EnvGen.ar(Env.perc(0, 0.1), a);
		c = SinOsc.ar(freq*2.5, 0, b);
		o = SinOsc.ar(freq, c, b);
		o = Pan2.ar(o, 0, amp);
		^o
	}
}

Gucha {
	*ar { |in, len=2, rate=1|
		var a, b, o;
		a = LFNoise2.ar(rate, len/2, len/2);
		o = DelayL.ar(in, len, a);
		o = Pan2.ar(o, 0);
		^o
	}
}

これをコンパイルして以下を実行。

{ Gucha.ar(Pong.ar(500, 5, 0.5)) }.play;

他人が見たらなんのこっちゃと思うに違いないコード。

{ Gucha.ar(Pong.ar(500, 5, 0.5), rate:100) }.play;

音もなんのこっちゃ。

まとめ

Writing-Classesヘルプを見てますと、もっと難しいことが書いてあり私なぞにはよくわけがわかりません。
というかオブジェクト指向云々も未だよくわかってないのだが。
ともあれ、それらしきものを書いて利用するのはさほど難しくないのではというお話。