SuperCollider日記(6)

長嶋 洋一

(一部 Processing日記かも)

Part 1  Part 2  Part 3  Part 4  Part 5  Part 7 


2011年8月18日(木)

残暑が厳しい夏休みであるが、邪魔が入らないので こんなことこんなことこんなことこんなこと を進められる。 そして今日の夕方には、 山村知世・個展 のために焼津に出かける予定である。 焼津の美味しい海鮮をいただこうと、ちゃんと3日間も休肝して、準備万端である。 イギリス出張を挟んで、 SuperColliderProcessing もソコソコ進んだが、ようやくProcessingに続いてSuperColliderもOSCのところに来たので、ここで両者を合体して、 OSCを活用して、 「Max5 + Processing + SuperCollider」 という、一つの目標に到達してしまおう。

まずは思い出しとして、 Processing の「6月12日」のところにあった「Max5とProcessingの連携」をさらに改訂して、 Max5とProcessingのそれぞれのウインドウ内でのユーザインターフェースの情報が相互に相手に届いて、自分のウインドウ内で相手の動作が見える、 という「たすきがけ動作」を作ってみた。 この部分だけはSuperColliderでなく完全に「Processing日記」である(^_^;)。

以下のプログラムでは、 Max5側のパッチはudptest_07.maxpat である。 Max5パッチ内に400*400のjitterスクリーン画面を開いて、その中でボタンの有無と関係なく、 マウスカーソルの座標をリアルタイムに取得して、このx,y座標をpackして、OSCで送っている。 ここまでは前の例と同じである。 そしてさらに、400*400のスクリーンの色は15秒ごとにランダムに変更して塗りつぶされる。 ここには、Max5側のマウス操作では何も描画できない、という点が重要である。

そして以下のProcessingスケッチでは、まず第一には前の例と同様に、アクティブなMax5パッチ内の400*400のスクリーン内部でマウスを動かすと、 OSC経由で受け取ったこの座標に対して、オブジェクトがこれを追いかけながらうろうろして、残像は消えて行く。 追いかける座標はMax5からもらっている座標である。 ところが、このProcessingスクリーンの方をアクティブにすると、なんとオブジェクトはそのProcessingスクリーン内のマウスカーソルを追いかける。 ただしこのプログラムで行っているのは、「Processingスクリーン内のマウス座標をOSC経由で送っている」処理である。


import oscP5.*;
import netP5.*;

OscP5 oscP5;
NetAddress myBroadcastLocation; 
Mover[] movers = new Mover[20];
int x_position = 200;
int y_position = 200;
int mouse_x_old, mouse_y_old;

void setup() {
	size(400,400);
	smooth();
	background(255);
	oscP5 = new OscP5(this,8000);
	myBroadcastLocation = new NetAddress("127.0.0.1",7000);
	for (int i = 0; i < movers.length; i++) {
		movers[i] = new Mover(); 
	}
	mouse_x_old = mouseX;
	mouse_y_old = mouseY;
}

void draw() {
	noStroke();
	fill(255,10);
	rect(0,0,width,height);
	for (int i = 0; i < movers.length; i++) {
		movers[i].update();
		movers[i].checkEdges();
		movers[i].display(); 
	}
	mouse_check();
}

void mouse_check() {
	if ((mouse_x_old == mouseX) && (mouse_y_old == mouseY) ) return;
	mouse_x_old = mouseX;
	mouse_y_old = mouseY;
	OscMessage myOscMessage = new OscMessage("Processing");
	myOscMessage.add(mouse_x_old);
	myOscMessage.add(mouse_y_old);
	oscP5.send(myOscMessage, myBroadcastLocation);
}


void oscEvent(OscMessage theOscMessage) {
	x_position = theOscMessage.get(0).intValue();
	y_position = theOscMessage.get(1).intValue();
}

class Mover {
	PVector location;
	PVector velocity;
	PVector acceleration;
	float topspeed;

	Mover() {
		location = new PVector(random(width),random(height));
		velocity = new PVector(0,0);
		topspeed = 4;
	}

	void update() {
		PVector mouse = new PVector(x_position,y_position);
		PVector dir = PVector.sub(mouse,location);
		dir.normalize(); // Normalize
		dir.mult(0.5);   // Scale 
		acceleration = dir;  // Set to acceleration
		velocity.add(acceleration);
		velocity.limit(topspeed);
		location.add(velocity);
	}

	void display() {
		stroke(0);
		fill(175);
		ellipse(location.x,location.y,16,16);
	}

	void checkEdges() {
		if (location.x > width) {
 			location.x = 0;
		} else if (location.x < 0) {
			location.x = width;
		}
		if (location.y > height) {
			location.y = 0;
		}  else if (location.y < 0) {
			location.y = height;
		}
	}
}
実はMax5側では、上記の処理に加えて、「OSCでProcessingから受け取った座標を中心とした円を、Max5パッチ内のスクリーンに描画」するだけでなく、 「OSCでProcessingから受け取った座標を、Max5パッチ内スクリーンで得たマウス座標の代わりにOSCで送信」しているのである。 アクティブなのはProcessingスクリーンである事に注意しよう。 つまりここで起きているのは、以下のような動きの連携となっている。

一見すると、Processingのスクリーン内で単にマウスカーソルを追いかけたインタラクティブなプログラム、と思えるが、 情報がOSCを経由してMax5に到達してMax5側でグラフィクスを駆動し、そこから再びOSCを経由して送られてきた座標を追いかけている、 という点が重要なのである。

これで復習と道具だてが出来たので、いよいよSuperColliderである。 Workshop materials for Computer Music (G6002)10. Network Music の最初、 10.1 Open Sound Control.html からということになる。 同じOSCなので「Processing日記」と解説が重複するところもあるが、ここは「SuperCollider日記」なので、あらためて追いかけていくことにしよう。 まずはいつものように以下のサーバの設定である。


Server.default=s=Server.internal;
ここでのOSCの説明は「OSC is a communication protocol used for sending information across a computer network.」 である。 SuperColliderは既にサーバタイプとなってネットワーク上で動作する構造となっているので、まったくスムースに繋がっている。 そしてなんと、「It is the lifeblood of SuperCollider since all communication from the language application to the localhost server uses OSC!」とあった、 つまりSuperColliderのlocalhost serverは、それ自体がOSCを使っているのであった。(^_^)

OSCを使うには、「OSC Address Space」が必要である。 これはファイルディレクトリの木構造と同様に、スラッシュで切って階層構造を記述する。 たとえば「http://www.cnmat.berkeley.edu/OpenSoundControl/OSC-spec.html」 とか「/myparameters/object3/frequency」 などである。

OSCのメッセージは全てテキストベースである。 バイナリデータは何らかの形でテキスト化することになる。 ストリングのタイプは「address, typetags, values」である。 OSCメッセージは、かたまりとして完結する単位が到着すると、速やかに処理系で処理されることになっている。 「bundle」というOSCコマンドの一群を送ると、タイムスタンプが添えられてストリーミングのような連続データを送ることもできるらしい。

OSCによって特定のIPアドレスに送る一般的な方法は[NetAddr]を見よ、とある。 この定義は以下のようなものである。

NetAddr			network address

superclass: Object


	*new(hostname, port)	create new net address. 
				Hostname is a string, either an ip number (e.g. "192.168.34.56") 
				or a hostname such as "otherHost.local".
				Port is a port number, like 57110.
				Note: to send messages internally, loopback ip is used: "127.0.0.1"
	
	*fromIP(ip, port)	create new net address using an integer ip number.
	
	*langPort		Get the port sclang is currently listening on (may change after a recompile).
	
	*localAddr		Get a NetAddr which corresponds to localhost and the port sclang is listening on.
	
	sendMsg(args...)		send a message without timestamp to the addr.
	
	sendBundle(timestamp, args...)	send a bundle with timestamp to the addr.
	
	sendRaw(rawArray)	send a raw message without timestamp to the addr.
	
	connect(disconnectHandler)		open TCP connection. disconnectHandler is called when
				the connection is closed (either by the client or by the server)
	
	disconnect			close TCP connection
	
	ip 			returns the ip number (as string)
				example: 
				n = NetAddr("localhost", 57110);
				n.ip;
	
	*disconnectAll			close all TCP connections
	
	*broadcastFlag			check broadcast flag
	
	*broadcastFlag_( flag )			Sets the broadcast flag (whether or not 
				broadcast messages can be sent)
	
	*useDoubles_( flag )			

// example

n = NetAddr("127.0.0.1", 57120); // 57120 is sclang default port
r = OSCresponder(n, '/good/news', { arg time, resp, msg; [time, msg].postln }).add;

n.sendMsg("/good/news", "you", "not you");
n.sendMsg("/good/news", 1, 1.3, 77);

n.sendBundle(0.2, ["/good/news", 1, 1.3, 77]);

r.remove;
n.disconnect;

// note that different NetAddr objects with the same port and ip are independent.

r = OSCresponder(nil, '/x', { "message arrived".postln }).add;

n = NetAddr("127.0.0.1", 57120);
n.sendMsg("/x")

u = NetAddr("127.0.0.1", 57120);
u.sendMsg("/x");

n.disconnect

u.sendMsg("/x");

r.remove;
u.disconnect;
SuperColliderのdefaultのポート番号は「57110」だそうである。 とりあえず、Max5で「ポート6000で受けた情報をMaxウインドウに表示」というプログラムを走らせた上で、 ポート番号を6000に変えた以下のサンプルを走らせてみると、何やらMax5で受信して表示した。 とりあえず「OSCで送る」方は、まず、繋がった(^_^)。

//demo to send to the localhost Server

(
	var n, id;
		//loopback address is 127.0.0.1- try substituting the 
		//IP address of another user's machine

	n=NetAddr("127.0.0.1", 6000); 
		//57110 is the port number; this is the standard 
		//number used by the Server

	id=s.nextNodeID;
	n.sendMsg("s_new",\default,id,0,0);   
		//sendMsg works out the correct OSC message for you

	SystemClock.sched(1.0,{n.sendMsg("n_free",id); });
)

SuperColliderは、どちらかと言えばマスタ側よりもスレーブ側として、OSCで制御信号を受け取ってサウンド処理サーバとして走る構想であるが、 たしかショボいGUIを送ることも出来たので、これをやってみる事にした。 材料としたのは、「SuperCollider(3)」にあったサンプルから音源を取り去った、以下である。


(
	var w, slid2d;
	w=Window("test", Rect(100,500,300,300)); 
	slid2d= Slider2D(w,Rect(5,5,285,285));
	slid2d.action_(
		{
			[slid2d.x, slid2d.y].postln;
		}
	);
	w.front;
)

これをさっきのOSCデモと合体させて、さきのサンプルでProcessingから送られてきた座標の代わりにMax5に送ってやろう、という事である。 そして、以下のように、マウスの座標を浮動小数点で送っているものの、無事にSuperColliderの2次元スクリーンでマウスぐりぐりして、 Max5がProcessingと同様に反応してグラフィクスを描いた(^_^)。 ただしSuperColliderの座標は「yは下から上」ということで、マウスの上下の動きとスクリーンの動きが上下で鏡像となった。(^_^;)


(
	var w, slid2d;
	var n, id;
	n=NetAddr("127.0.0.1", 7000);
	id=s.nextNodeID;

	w=Window("test", Rect(440,500,400,400)); 
	slid2d= Slider2D(w,Rect(5,5,385,385));
	slid2d.action_(
		{
			n.sendMsg(
				"SuperCollider",
				400 * slid2d.x,
				400 * slid2d.y
			);
		}
	);
	w.front;
)

そして、以下が、ここにさきほどのProcessingスケッチも同時に走らせたところである。 もうスクリーン内に足の踏み場も無いが(^_^;)、 「Max5 + Processing + SuperCollider」 が連携して、以下のような動作をちゃんとやっている。 SuperColliderのスクリーンの上下とは、Max5のパッチ内、そしてProcessingのスクリーンでの動きの上下が反転している。

SuperCollider serverを制御するための標準的なメッセージについては、[Server]のヘルプを見よ、とあったが、 こんなに あったので軽くパスしておく(^_^;)。 また、他のコンピュータに対してNetAddrsを経由して一斉配信するBroadcastServer classというのもあるらしいが、 これも当面は深入りしない。 なんせ、「とりあえず使えてしまう」のが、OSCのいいところなのだ。

・・・ここでぼちぼち時間となってきた。 まだSuperColliderでOSCメッセージを受けていないが、続きはまた後日である。

2011年8月21日(日)

雨が降って、めっきりと涼しくなってきた。 前回の日記のあとで「くるる」に乗って、焼津の 山村知世 個展2011 に出かけて、 翌日19日に朝帰りしてゼミの奥山さん制作を手伝い、曽根クンのアポを受けて相談したりした。

そして昨日20日と今日であるが、ここに詳細は書けないものの、色々と実験などを進めていた。 マツダのディーラーに何度も電話して技術資料をFAXしてもらったり、 電ドラを持ち出して駐車場のクルマに潜り込んだりボンネットを開けたりしていろいろ改造し、 さらにオシロスコープを車内に持ち込んで駐車場内をグルグル走ったりした。 涼しくなってくれたので助かったが、こちらはまだまだ難航している。 再びディーラーに電話して宿題を出したので、明日あたりに連絡が来るまで、ぽっかりと時間が空いてしまったので、 ちょっとだけでもSuperColliderを進めてみることにしよう。

「SuperColliderでOSCを出す」というのは上手くいったので、次は「SuperColliderでOSCを受ける」である。 Processingでのトラブルで学んだ教訓から、「同じポート番号でOSCをモニタする複数のプロセスが無いようにする」というところに注意である。 せっかくなので、Max5だけでなく、その情報を受けるProcessingスケッチも一緒に走らせることにする。 つまり当初の構想のように、GainerやMIDIのセンサ情報を受けてリアルタイム処理するのがMax5であり、 ここからグラフィクスのProcessingと、サウンドのSuperColliderにOSC経由で情報を送る、というシステムである。 もちろん、Processingのライブカメラ処理(画像センサ)の情報をOSCでMax5に、というのもアリである。 jitterの画像センサ処理能力はややProcessingに見劣りするので、このような組み合わせは効果的だろう。

スタートラインは以下である。 スクリーン内の配置は変わったが、Max5のパッチ、Processingのスケッチ、そしてSuperColliderのプログラムは全て、上記の最新のもの、 そのままである。 SuperColliderのGUI画面、さらにProcessingのスクリーン、そしてMax5のパッチ内のいずれでマウスカーソルを動かしても、 Max5のパッチ内とProcessingのスクリーンで、マウスの操作に対応した描画が行われている。 ここで確認したいのは、ポート7000でOSCパケットをモニタしているのはMax5だけなので、「他に同じポートで受信する子がいる」と拗ねる子もなく(^_^;)、 全体の動作には問題はないが、このポート7000でOSCパケットを送っている子は、ProcessingとSuperColliderの両方である、という点である。 つまり、「同じポートで受ける子」は1人に限るが、「同じポートに出す子」はいくらあってもOKなのである。

ちなみに上のような状態で、SuperColliderのGUI画面内でマウスをぐりぐりやっている状態でのアクティビティモニタのCPU使用率は以下であった。 SuperColliderはサーバだけだと処理は軽いが、さすがにGUIとかOSCが加わるとソコソコになる。 Max5の30%というのも、なかなかの数字であるが、Processingはさすがに裏でJavaがガンガン走っているので、相当なものである。 関連したウインドウサーバの処理も含めて、この3つのアプリで総計100%近くに行くとすれば、デュアルコアのマシンは最低限、必要だろうか。

さて、 Workshop materials for Computer Music (G6002)10. Network Music10.1 Open Sound Control.html の続きからである。 OSCのメッセージを受け取り反応するSuperCollider言語のクラスは、以下の「OSCresponder class」であるという。 なかなかにいかめしくて大変そうである(^_^;)。

OSCresponder				client side network responder

Register a function to be called upon receiving a specific c
ommand from a specific OSC address.

Other applications sending messages to SuperCollider should distinguish 
between sending messages to the server or the language. Server messages 
are documented in the Server-Command-Reference and should be sent to 
the server's port - s.addr.port - which is 57110 by default. Messages sent 
to the server will not be processed by any OSCresponder in the language.

Messages from external clients that should be processed by OSCresponders 
must be sent to the language port, 57120 by default. Use NetAddr.langPort 
to confirm which port the SuperCollider language is listening on.

See OSC_communication for more details.

Important: It is highly recommended to use OSCresponderNode or 
OSCpathResponder instead of OSCresponder directly. OSCresponders can 
overwrite each other, but OSCresponderNodes with the same address and 
command name can coexist peacefully.

Examples: see OSCresponderNode

	*new(addr, cmdName, action);

	addr		
		the address the responder receives from (an instance of NetAddr, e.g. Server.default.addr)
		an address of nil will respond to messages from anywhere.
		an address with a port of nil will respond to messages from any port from that specific IP.
	
	cmdName
		an OSC command eg. '/done'
	
	action
		a function that will be evaluated when a cmd of that name is received from addr.
		arguments: time, theResponder, message, addr
		note that OSCresponder evaluates its function in the system process.
		in order to access the application process (e.g. for GUI access ) use { ... }.defer;
		within the action function, e.g., { |time, resp, msg| { gui.value_(msg[3]) }.defer }
				
Note:
	A single OSCresponder may be set up for each addr and cmdName combination.  
	Subsequent registrations will overwrite previous ones.  See OSCresponderNode.

	Whenever an OSC message is sent to the SuperCollider application (the language, 
not the server), either 	Main-recvOSCmessage or Main-recvOSCbundle is called.  
There, the messages are forwarded to the 	OSCresponder class using the 
OSCresponder-respond class method.  

	add
		add this responder instance to the list of active responders.  
		The OSCresponder is not active until this is done.

	remove
		remove and deactivate the OSCresponder

	removeWhenDone
		remove and deactivate the OSCresponder when action is done.
	
	//syntax:	
	OSCresponder(addr,cmdName,action).add.removeWhenDone;
	
	*add(oscResponder)
		add the responder instance 
	
	*remove(oscResponder)
		remove the responder instance 
	
	*removeAddr(addr)
		remove all OSCresponders for that addr.
これに続く詳細不明のサンプルの最後に、「色々なOSCメッセージを受けるには[OSCresponderNode]を参照せよ」とあった。 そこで、[OSCresponderNode]の中にあるサンプルを試してみることにしよう。 まず[OSCresponderNode]の冒頭の解説は以下である。
OSCresponderNode			client side network responder

Register a function to be called upon receiving a specific command 
from a specific OSC address. same interface like OSCresponder, but 
allows multiple responders to the same command.

Note that OSCresponderNode evaluates its function in the system process.
In order to access the application process (e.g. for GUI access ) use { ... }.defer;

Other applications sending messages to SuperCollider should distinguish 
between sending messages to the server or the language. Server messages 
are documented in the Server-Command-Reference and should be sent to 
the server's port - s.addr.port - which is 57110 by default. Messages sent 
to the server will not be processed by any OSCresponder in the language.

Messages from external clients that should be processed by OSCresponders 
must be sent to the language port, 57120 by default. Use NetAddr.langPort 
to confirm which port the SuperCollider language is listening on.

See OSC_communication for more details.
そしてこの最後にあるように、より詳しくは[OSC_communication]参照せよ、との事である。 この[OSC_communication]の解説は以下である。 これを「リファレンス地獄」という。(^_^;)
OSC_communication

OSC communication between programs is often done to send messages 
from one application to another, possibly with the applications running on 
different computers. In SuperCollider this communication is done by creating 
a NetAddr of the target application and creating an OSCresponderNode to 
listen to another application. The underlying protocol of OSC is either UDP or TCP.

Sending OSC to another application

To establish communication to another application, you need to know on 
which port that application is listening. For example if an application is listening 
on port 7771, we can create a NetAddr and send it a message:

	b = NetAddr.new("127.0.0.1", 7771); 
		// create the NetAddr
	b.sendMsg("/hello", "there");
		// send the application the message "hello" with the parameter "there"

Receiving OSC from another application

To listen to another application, that application needs to send a message 
to the port SuperCollider is listening on, normally this is 57120, but it can 
change. The current port can be retrieved with
	
	NetAddr.langPort; // retrieve the current port SC is listening to

Or you can retrieve both the IP and the port with:

	NetAddr.localAddr; // retrieve the current IP and port

To listen to incoming messages, an OSCresponderNode needs to be created 
in SuperCollider. If the sending application has a fixed port it sends message 
from, you can set the OSCresponderNode to listen only to messages coming 
from that IP and port:

	n = NetAddr.new("127.0.0.1", 7771); // create the NetAddr
	// create the OSCresponderNode
	o = OSCresponderNode.new(n, "/goodbye", { arg time, resp, msg; [time,resp].postln; } ).add; 
	o.remove; // remove the OSCresponderNode when you are done.

Why OSCresponderNode rather than OSCresponder? If you create two 
OSCresponders with the same message name -- e.g. "/goodbye" above 
-- the second OSCresponder will overwrite the first. You can have only one 
OSCresponder per message name at the same time. OSCresponderNodes 
do not have that restriction -- many OSCresponderNodes with the same 
name can coexist. (This also means you have to keep track of your 
OSCresponderNodes to remove them when they are no longer needed.) 
See also OSCpathResponder.

Receiving from an application that is sending from a variable port

Some applications (notably Pd and Max) do not send messages from a 
fixed port, but instead use a different port each time they send out a 
message. In that case the OSCresponderNode needs to be set up, so 
that it listens to messages coming from anywhere:

	o = OSCresponderNode.new(nil, "/goodbye", { arg time, resp, msg; [time,resp].postln; } ).add; 
		// create the OSCresponderNode
	o.remove; 
		// remove the OSCresponderNode when you are done.

Testing incoming traffic

All incoming OSC messages call the message recvOSCmessage, or 
recvOSCbundle in Main. 
To see the incoming traffic, one may set a function called recvOSCfunc in Main:

// post all incoming traffic except the server status messages
(
thisProcess.recvOSCfunc = { |time, addr, msg| 
	if(msg[0] != 'status.reply') {
		"time: % sender: %\nmessage: %\n".postf(time, addr, msg); 
	}  
}
);

// stop posting.
thisProcess.recvOSCfunc = nil;
さて、道具だてである。 スタートラインのMax5側のパッチはudptest_08.maxpat である。 これは以前の「udptest_07.maxpat」と機能的には変わらないが、 ポート7000からの入力をMaxウインドウでモニタ表示するようにした。 そして出力情報はProcessingとSuperColliderが同じポートの設定では受けられない(拗ねる(^_^;))ので、 まったく同じ情報をポート8000に加えて、ポート9000からも出すようにしただけである。 また、Processingのプログラムは変更ナシ、そのままである。 一応テキスト保存したProcessingスケッチが これ である。

・・・ここで色々と実験すること約1時間、どうにもなかなかうまく行かない(^_^;)。 そしてフト、SuperCollider日記のだいぶ初期に、たしか赤松武子さんのSuperColliderサイトの話題として、 「ProcessingとSuperColliderとをOSCで連携させる」 というのがあったのを思い出した。 発掘モードに入ってしばし、 このページ の記述に辿り着いた。 既にここに出ている「oscP5」とか「p5_sc」は、環境に入っている(^_^)。 そこで作戦変更で、紹介されている このページ の「OpenSoundControl」のところに 「xiOSC_Processing」というのを発見した。 ここにあった、

In our workshops we go into how to communicate between networked 
computers and between different programming environments. The communication 
takes place using the Open Sound Control (OSC). Here you have some example 
patches in SuperCollider, Pure Data (PD), Max/MSP and a Processing program 
that sends and receives OSC from the sound programming environments. 
というのは、まさに欲しいモノである(^_^)。 そこで、「Download ixiOSC_Processing 」をクリックして、無事に これ をゲットした。 解凍してみると、中身は以下のようになっていた。 この中のMaxパッチ「Max-Processing.pat」は、Max5ではちょっと対応していない古いものだったが(中身に目新しいところナシ)、 SuperColliderのプログラム「SC-Processing.rtf」と、Processingのスケッチ「ixiOSCp55.pde」はとても有用だった。

そこで、このSuperColliderのプログラム「SC-Processing.rtf」と、Processingのスケッチ「ixiOSCp55.pde」 とを眺めながら、自前のProcessingスケッチを修正して、「ixiOSCp55.pde」と同様にOSCメッセージを送るように改造を目指した。 受け手のSuperColliderプログラムによって、マウス位置に対応してサウンドが鳴る動作は同等である。 まず、OSCを受けてサウンドを鳴らし、postウインドウにメッセージをモニタ表示するSuperColliderプログラムは以下である。


(
	SynthDef(
		\p55synth, 
			{
				arg freq= 440;
				var signal, env;
				env = EnvGen.ar(Env.perc, doneAction:2);
				signal = SinOsc.ar([freq, freq+1], 0, 0.8) * env;
				Out.ar(0, signal);
			}
	).load(s);

	r = OSCresponder(
		nil, 
		'/test', 
			{ 
				arg time, resp, msg; 
				[msg].postln;
				Synth(
					\p55synth, 
						[
							\freq, 600 - msg[1]
						]
				);	
			}
	).add;
)
そして、ウインドウ内をクリックすると、その座標をSuperColliderに送って鳴らすProcessingスケッチは以下である。 ここでは、Max5にはポート7000で、そしてSuperColliderにはデフォルトのポート57120で送っている。

import oscP5.*;
import netP5.*;

OscP5 oscP5;
NetAddress myBroadcastLocation1, myBroadcastLocation2; 
Mover[] movers = new Mover[20];
int x_position = 200;
int y_position = 200;
int mouse_x_old, mouse_y_old;

void setup() {
	size(400,400);
	smooth();
	background(255);
	oscP5 = new OscP5(this,8000);
	myBroadcastLocation1 = new NetAddress("127.0.0.1",7000);
	myBroadcastLocation2 = new NetAddress("127.0.0.1",57120);
	for (int i = 0; i < movers.length; i++) {
		movers[i] = new Mover(); 
	}
	mouse_x_old = mouseX;
	mouse_y_old = mouseY;
}

void draw() {
	noStroke();
	fill(255,10);
	rect(0,0,width,height);
	for (int i = 0; i < movers.length; i++) {
		movers[i].update();
		movers[i].checkEdges();
		movers[i].display(); 
	}
	mouse_check();
}

void mouse_check() {
	if ((mouse_x_old == mouseX) && (mouse_y_old == mouseY) ) return;
	mouse_x_old = mouseX;
	mouse_y_old = mouseY;
	OscMessage myOscMessage1 = new OscMessage("Processing");
	OscMessage myOscMessage2 = new OscMessage("test");
	myOscMessage1.add(mouse_x_old);
	myOscMessage1.add(mouse_y_old);
	oscP5.send(myOscMessage1, myBroadcastLocation1);
	myOscMessage2.add(mouse_x_old);
	myOscMessage2.add(mouse_y_old);
	oscP5.send(myOscMessage2, myBroadcastLocation2);
}

void oscEvent(OscMessage theOscMessage) {
	x_position = theOscMessage.get(0).intValue();
	y_position = theOscMessage.get(1).intValue();
}

class Mover {
	PVector location;
	PVector velocity;
	PVector acceleration;
	float topspeed;

	Mover() {
		location = new PVector(random(width),random(height));
		velocity = new PVector(0,0);
		topspeed = 4;
	}

	void update() {
		PVector mouse = new PVector(x_position,y_position);
		PVector dir = PVector.sub(mouse,location);
		dir.normalize(); // Normalize
		dir.mult(0.5);   // Scale 
		acceleration = dir;  // Set to acceleration
		velocity.add(acceleration);
		velocity.limit(topspeed);
		location.add(velocity);
	}

	void display() {
		stroke(0);
		fill(175);
		ellipse(location.x,location.y,16,16);
	}

	void checkEdges() {
		if (location.x > width) {
 			location.x = 0;
		} else if (location.x < 0) {
			location.x = width;
		}
		if (location.y > height) {
			location.y = 0;
		}  else if (location.y < 0) {
			location.y = height;
		}
	}
}

これで、ProcessingからSuperColliderにOSCメッセージを送って、モニタ表示とサウンド生成を制御できたことになる。 Max5から同等のプロトコルで送るのは簡単なので、これは後日の楽しみにとっておくことにしよう。

2011年8月31日(水)

前回から10日が経過した。いろいろ忙しくてSuperColliderを進めることもなく、受託研究も某ディーラーの不備?に翻弄されて進まず、しかし「35虎」 など新しい進展もあった。 そしてまたまた、セントレアから成田に向かうフライトの機内からの再開である(^_^;)。 今年は6月(NIME)・8月(ICMC)・9月(アルス)と海外出張が続いたが、これでたぶんオシマイである。 成田に飛び、フランクフルトに飛び、リンツに向かう。

SuperColliderの方は、OSCとの連携がほぼ片付いたので、 Workshop materials for Computer Music (G6002)10. Network Music10.2 Messaging Style.html からという事になる。 このセクションは、オブジェクト指向のSuperColliderらしく、「Messaging Style and Abstractions」ということで、メッセージに関する抽象化のお話らしい。

まず「SC3 philosophy」ときた。SuperColliderの哲学である。 直接にアプリケーションとしてサウンド処理を行ってきたSuperCollider2までと違って、SuperCollider3では、サーバがサウンドの合成・生成などを行うのと分離されて、「いつ」「どのような」サウンドのイベントが起きるのか、を指定する。このため、バックグラウンドではSuperColliderアプリ(SuperCollider言語で記述している部分)から信号処理のSuperColliderサーバへの一定のストリームが常に走っている。

コンビュータネットワーク上には多数のサーバが走っている。そして我々はネットワーク・オーディオ・プロトコルであるOSCを使って、それらサーバと通信できる。 この体系の短所として、SuperCollider言語アプリケーションと"localhost"やサーバ群との通信には、必ずなんらかの遅延(レイテンシ)が存在する。 しかし長所として、SuperCollider言語アプリケーションとサーバの片方がクラッシュしても、もう片方は関係なく生きている。 これはSuperCollider2までの場合と違うところで、ライブコンサートでSuperCollider2を使っていた時には「ステージ上で全てが固まる(^_^;)」という恐怖の可能性があったが、SuperCollider3となって大きく改善されたわけである。

SuperColliderには、SuperCollider言語アプリ自身でその動作を検証できるように"internal"サーバもあるが、これはこのセクションで扱う「ネットワーク音楽」をサポートしていないので、ここでは"localhost"サーバ、あるいは他のlocalなサーバを使うことになる。

・・・さて数時間後、ロシア上空で目覚めての再開である。トピックは「Running SC3 by passing commands to the Server- Messaging Style」である。 SuperColliderでサウンドを鳴らす、という事だけであれば簡単な方法とは言えないが、SuperColliderにおいて言語アブリからサーバにメッセージを送ることでサウンドを鳴らす、という状況を理解するためのサンプルから学んでいこう。 環境設定としては、起動していなければlocalhostサーバを起動する、ということで、以下のような事になる。


s=Server.local; if(not(s.serverRunning), {s.boot});
サウンドをシンセサイズするためには、SynthDefsをサーバに投げればよい。 以下の例では、UGen Graphsを構築して、SuperCollider言語アブリはこれをサーバが理解できるようにコンバイルする。 実行するとpostウインドウに「a SynthDef」と表示されてコンパイル成功を確認できる。 ここで定義したSynthDefは、後で必要に応じてインスタンス呼び出しで鳴らすことが出来る。

SynthDef(
	"sine", 
		{
			arg freq=440;
			var osc;
			osc = SinOsc.ar(freq, 0, 0.1); 
				//sine oscillator
			Out.ar(0, osc); 
				// send output to audio bus zero.
		}
).writeDefFile;
	//place this SynthDef on disk in compiled form in the 'synthdefs' folder 

ここで、以下のように「/d_load」を送るとSynthDefがロードされる。


s.sendMsg("/d_load","synthdefs/sine.scsyndef"); 
	//server loads the SynthDef
そして、以下のように「/s_new」を送るとSynthDefインスタンスが具体的に発音を開始する。 「s」はlocalhostサーバに付けた名前である。

s.sendMsg("/s_new", "sine", 1000, 1, 0); 
	//start a Synth instance using the SynthDef- it is created
	//as a Synth Node at the tail of the RootNode Group
ここでさらに以下のように「/s_new」を送ると、パラメータを変えて新たなサウンドを重ねて生成して鳴らすことが出来る。

s.sendMsg("/s_new", "sine", 1001, 3, 1000, \freq, 450);
以下のように「/n_set」を送ると、鳴っている音のパラメータを変えることができる。 「n」はノード番号である。

s.sendMsg("/n_set", 1001, "freq", 900);
そして、以下のように「/n_free」で、インスタンスを解放してサウンドを消すことができる。 「n」はノード番号である。

s.sendMsg("/n_free", 1000);   //stop Node 1000

s.sendMsg("/n_free", 1001);   //stop Node 1001
これ以上の詳しいことは[Server-Command-Reference]に書いてあるという。 これ である。

次のトピックは「Using Classes that encapsulate those commands」である。 前述のメッセージ・パッシングはSuperColliderの本質であるが、これは見た目には複雑なので、 中身はそのままなのに、カプセル化して隠して、見た目はそれと感じないような体系があるという。 まずは以下の新しいSynthDefを、今度はサーバに直接ロードさせて定義する。


SynthDef(
	"LFPar", 
		{
			arg freq=440, pan=0.0;
			var osc;
			osc = LFPar.ar(
				LFPar.kr(
					ExpRand.new(1,80),
					0,
					freq*0.02,
					freq
				), 
				0, 
				0.1
			); 
			Out.ar(0, Pan2.ar(osc,pan));
		}
).load(s);
以下の例では、ノード番号などを伏せて次々にサウンドを生成している。 こんな音がした。

a= Synth("LFPar"); 
	//make a Synth, let some hidden code worry about Node numbers...

b=Synth("LFPar", [\freq, rrand(200,700), \pan, 1.0.rand2]); 
	//make another Synth, pass some values to the arguments

RootNode.freeAll;
	//stop all Synths from the top level Group, or press cmd+period

次のトピックは「{}.play」である。これはメッセージを送る、という体系をより隠して直感的にしたもので、言語アプリケーション内でサウンドを生成しているように見える、SuperCollider2の時代から登場してきている書式である。 しかしこれもwrapperであり、内部的にはSuperColliderはサーバにメッセージを送ることでサウンドを生成している。 以下の例では、直接に2音を生成し、マウス操作でぐりぐりしている。 こんな音がした。


{SinOsc.ar([MouseX.kr(440,880), MouseY.kr(330,660)],0,0.1)}.play

{SinOsc.ar([MouseX.kr(220,770), MouseY.kr(110,550)],0,0.1)}.play

以下の例では、「15秒が経過したらサウンドを止める」というスケジューラまでを一気に定義した「自動演奏」となっている。 こんな音がした。


(
	var synth;
	synth= {
		LPF.ar(
			Pan2.ar(
				CombN.ar(
					Impulse.ar(9, 0.5, 0.1),
					0.2,
					0.2, 
					30
				), 
				MouseX.kr(-1.0,1.0)
			), 
			MouseY.kr(500,10000)
		)
	}.play(RootNode.new);
	SystemClock.sched(
		15,
		{synth.free; nil}
	);
)

簡単であるが、この「{}.play」の危険なところは、呼び出されるたびに新たにSynthDefをどんどん定義してしまうところである。 メッセージとしてインスタンスを起動すれば、そのノードを管理して発音が終わった時にはメモリを解放できるが、例えば無限ループのような「自動演奏」プログラムの中でこれを使うとタイヘンな事になる(^_^;)。

・・・これで、このセクションは終わりである。 次の「10.3 Network Music Lab.html」は、どうも後から追加されたセクションのようで、実験してみたがうまく動かないのでパスすることにした(^_^;)。 その次は、いよいよアドバンスに進む「synthesis 4」である。 フライトはぼちぼちモスクワ上空、日本時間は19時、現地時間は12時、あと4時間ほどのフライトである。 リンツへの乗り継ぎのフランクフルト空港でも、またまた時間はタッブリある。

2011年9月1日(木)

長い長い8月31日から夜が明けて、9月1日の朝7時半である。もっとも日本はもう午後2時半である。 昨日はフランクフルト空港のカフェで、フランクフルトソーセージにドイツビール、という本場の味を堪能したが、 4時間の待ち合わせでリンツ行きのフライトに乗る頃には体内の日本時間が深夜2時となり、睡魔とともにリンツ入りとなった。 今年のホテルは市内中心にある、カジノを併設している立派なところだが、ネット予約と円高効果で、それほど高価でもなかった。

今日はアルスエレクトロニカ取材の初日だが、まずはブルックナーハウスに行ってフェスティバルパスを受け取らないとどこにも行けず、このチケット窓口が10時からopenなので、それまではホテルでSuperColliderでもやっているしかない。 Workshop materials for Computer Music (G6002) の 「11. Sound Synthesis 4」 の 11.1 Physical Modelling.html から、いよいよ物理モデルの楽音合成である。 前のセクションのネットワークサーバの話題は終わったので、ここでは以下のようにinternalサーバに戻している。


Server.default=s=Server.internal;
s.boot;
サウンドの合成は生楽器に反映させることから始まっているので、楽器音響の物理的な考察は重要である。 物理モデルによる楽音合成アルゴリズムは、物理的な現象を記述する数学的な等式によって実現される。 これはかなり作り上げるのがしんどくて、さらに制御パラメータも膨大であるが、単なる味気ないサンプリングサウンドに比べて、 リアルで表情豊かな楽音を合成できる可能性がある。

物理モデル楽音合成のパラメータは、音楽演奏家の感覚的な言い方でなくて、「唇のテンション」「弦のクロスセクション領域」などエンジニアの言葉で記述されている。音楽的(感覚的)なパラメータで物理モデル音源を制御することは、現在も研究中の領域である。 物理モデル音源には、以下のような色々なタイプがある。

ここではこれらの詳細には深入りしない。いまだ研究途上の領域であり、きちんとリアルタイムに行うにはコンピュータパワーも相当に必要である。 とりあえずここでは音響学的な物理モデル楽音合成のサンプルとして、Youngの定理に基づく「stiff string」のモデルを鳴らしてみよう。 1本のナイロン弦が、その密度と半径とが与えられた場合にどう振動するか、という物理的な方程式をそのまま計算して、共振周波数と減衰の様子を再現したものである。スティール弦の場合はコメントに書かれている。 盛大な道具だての末にたった1音「ポーン」と鳴るだけだが(^_^;)、 こんな音がした。

//adapted from 2.18 Vibrations of a Stiff String, p61, 
//Thomas D. Rossing and Neville H. Fletcher (1995) Principles 
//of Vibration and Sound. New York: Springer-Verlag 

(
	var modes,modefreqs,modeamps;
	var mu,t,e,s,k,f1,l,c,a,beta,beta2,density;
	var decaytimefunc;
	var material;
	material= \nylon; // \steel
		//don't know values of E and mu for a nylon/gut string
		//so let's try steel
		//radius 1 cm
	a=0.01;
	s=pi*a*a;	//radius of gyration
	k=a*0.5;
	if (material ==\nylon,
		{
			e=2e+7; 
			density=2000; 
		},{
			//steel
			e= 2e+11; //2e+11 steel;
				//density p= 7800 kg m-3 
				//linear density kg m = p*S
			density=7800; 
		}
	);
	mu=density*s;
	t=100000;
	c= (t/mu).sqrt;  //speed of sound on wave
	l=1.8; //0.3
	f1= c/(2*l);
	beta= (a*a/l)*((pi*e/t).sqrt);
	beta2=beta*beta;
	modes=10;
	modefreqs= Array.fill(
		modes,
			{
				arg i; 
				var n,fr;
				n=i+1;
				fr=n*f1*(1+beta+beta2+(n*n*pi*pi*beta2*0.125));
				if(fr>21000, {fr=21000}); //no aliasing
				fr
			}
	);
	decaytimefunc= 
		{
			arg freq;
			var t1,t2,t3;
			var m,calc,e1dive2;	//VS p 50 2.13.1 air damping
			m=(a*0.5)*((2*pi*freq/(1.5e-5)).sqrt);
			calc= 2*m*m/((2*(2.sqrt)*m)+1);
			t1= (density/(2*pi*1.2*freq))*calc;
			e1dive2=0.01; //a guess!
			t2= e1dive2/(pi*freq);	//leave G as 1
			t3= 1.0/(8*mu*l*freq*freq*1);
			1/((1/t1)+(1/t2)+(1/t3))
		};
	modeamps=Array.fill(
		modes,
			{
				arg i; 
				decaytimefunc.value(modefreqs.at(i))
			}
	);
	modefreqs.postln;
	modeamps.postln;
	{
		var output;
		output=EnvGen.ar(
			Env.new([0,1,1,0],[0,10,0]),
			doneAction:2
		)*Mix.fill(
			modes,
				{
					arg i; 
					XLine.ar(1.0,modeamps.at(i),10.0)*SinOsc.ar(modefreqs.at(i),0,1.0/modes)
				}
		);
		Pan2.ar(output,0)
	}.play;
)

さて、物理モデル楽音合成のトレンドとしては、以下の「非線形駆動源」+「非線形共鳴体」という構図も重要である。

エキサイターとはつまり、サウンドのエネルギー源であり、レゾネータとはつまり、楽器を特徴付けている共鳴(振動を拡大させて空気振動に変換する)機構である。 物理モデルでは往々にして、楽器本体の振動を数式化することで、ピックアップの検出する振動を記述してしまうが、これで空気振動として拡張する部分が欠落する。この場合、適宜、リバーブ等のモデルを付加してやる必要がある。 例えばピアノの場合、以下のようにハンマーの打撃は短時間で、その後にサウンドが生まれてくる。


(
	// this shows the building of the piano excitation function used below
	{
		var strike, env, noise;
		strike = Impulse.ar(0.01);
		env = Decay2.ar(strike, 0.008, 0.04);
		noise = LFNoise2.ar(3000, env);
		[strike, K2A.ar(env), noise]
	}.plot(0.03); //.scope
)

以下は、櫛型フィルタによるレゾネータの無い、打撃のインパルスだけの音である。 こんな音がした。


(
	// hear the energy impulse alone without any comb resonation
	{
		var strike, env, noise;
		strike = Impulse.ar(0.01);
		env = Decay2.ar(strike, 0.008, 0.04);
		noise = LFNoise2.ar(3000, env);
		10*noise
	}.play
)

以下は、櫛型フィルタによるレゾネータを付けた、単音の打撃の音である。ピアノの高音域を想定して3本の弦を叩いている。 こんな音がした。


	//single strike with comb resonation 
(
	{
		var strike, env, noise, pitch, delayTime, detune;
		strike = Impulse.ar(0.01);
		env = Decay2.ar(strike, 0.008, 0.04);
		pitch = (36 + 54.rand); 
		Pan2.ar(
			// array of 3 strings per note
		Mix.ar(
			Array.fill(
				3, 
					{ 
						arg i;
							// detune strings, calculate delay time :
						detune = #[-0.05, 0, 0.04].at(i);
						delayTime = 1 / (pitch + detune).midicps;
							// each string gets own exciter :
						noise = LFNoise2.ar(3000, env); 
							// 3000 Hz was chosen by ear..
						CombL.ar(noise, 
							// used as a string resonator
							delayTime, // max delay time
							delayTime, // actual delay time
							6
						) 	// decay time of string
					}
				)
			),
			(pitch - 36)/27 - 1 
				// pan position: lo notes left, hi notes right
		)
	}.play
)

以下の例は、「James McCartney」氏(つまりSuperColliderの作者本人(^_^))が作った、ピアノサウンドの物理モデルである。 最初にハンマーでの短時間の打撃サウンドがあり、その後にフィルタによる共鳴のエミュレーションがあり、気合いの入ったリッチなサウンドとなっている。 こんな音がした。


(
		// synthetic piano patch (James McCartney)
	var n;
	n = 6; // number of keys playing
	play(
		{
			Mix.ar(
				Array.fill(
					n, 
					{ // mix an array of notes
						var delayTime, pitch, detune, strike, hammerEnv, hammer;
							// calculate delay based on a random note
						pitch = (36 + 54.rand); 
						strike = Impulse.ar(0.1+0.4.rand, 2pi.rand, 0.1); 
							// random period for each key
						hammerEnv = Decay2.ar(strike, 0.008, 0.04); 
							// excitation envelope
						Pan2.ar(
								// array of 3 strings per note
							Mix.ar(
								Array.fill(
									3, 
									{ 
										arg i;
											// detune strings, calculate delay time :
										detune = #[-0.05, 0, 0.04].at(i);
										delayTime = 1 / (pitch + detune).midicps;
											// each string gets own exciter :
										hammer = LFNoise2.ar(3000, hammerEnv); 
											// 3000 Hz was chosen by ear..
										CombL.ar(hammer, 
												// used as a string resonator
											delayTime, // max delay time
											delayTime, // actual delay time
											6
										) // decay time of string
									}
								)
							),
							(pitch - 36)/27 - 1 
								// pan position: lo notes left, hi notes right
						)
					}
				)
			)
		}
	)
)

・・・まだこのセクションの半分ぐらいである。物理モデルはなかなかに深い。 ここで時間になったので1年ぶりのリンツの街に出たが、まぁ、風景はいつものリンツだった。 いつものようにトラム/バスの1週間チケットを買い、ハウプト広場に行き、ブルックナーハウスに散歩してフェスティバルバスをゲット。 ドナウ川の畔には、去年は無かった巨大なPAと仕掛けのクレーン車が林立していて、今年はまた「花火とレーザーのショー」が復活したらしい(^_^)。 どうも時差ぼけがキツいので、初日は「時差慣れ日」と決めて、丘の上の教会まで登山電車で登ってリンツの町並みを眺めてのプチ観光、ハウプト広場に戻ってフェスティバルの半球形ドーム会場を覗いたところで、いったんホテルに戻った。 晩にアルスエレクトロニカセンター周辺で行われるライヴに行くまで、しばし休憩である。

そこで物理モデル合成の続きである。 上の例とも関係したシンプルな物理モデル楽音合成が、「Karplus-Strong synthesis」である。 これは、ノイズ源と、音高に対応した長さのディレイラインとから構成される。 ディレイラインをカスケードさせて楽音が減衰するまでフィルタ化させるループを構成すればよい。 上の例の櫛型フィルタも、実はディレイラインをループさせて構成しているので、その意味でも似た方式である。 ループさせたディレイラインは、周波数軸上で周期的にサウンドを急激に減衰させるので、生成される楽音の周期と関係する。 以下は、この「Karplus-Strong synthesis」を簡単お手軽にレディーメイド化した「Pluck」UGenである。 マウスのX座標に対応してパラメータが変化し、なかなかいいカンジの、 こんな音がした。


Server.default=s=Server.internal;
s.boot;

(
	{
		Pluck.ar(
			WhiteNoise.ar(0.1), 
			Impulse.kr(1), 
			440.reciprocal, 
			440.reciprocal, 
			10, 
			coef:MouseX.kr(-0.999, 0.999))
	}.play(s)
)

これは、個別のUGensとして分解して記述すれば以下のようになる。


(
	{
		var freq,time, ex, delay, filter, local;
		freq= 440;
		time= freq.reciprocal;
		ex= WhiteNoise.ar(
			EnvGen.kr(
				Env(
					[1.0,1.0,0.0,0.0], 
					[time,0,100]
				)
			)
		);
		local= LocalIn.ar(1);
		filter= LPZ1.ar(ex+local); //apply filter
		delay= DelayN.ar(
			filter, 
			time, 
			time-ControlDur.ir
		);
		// ControlDur.ir.poll;   
		LocalOut.ar(delay*0.95); 
		Out.ar(
			0, 
			Pan2.ar(filter,0.0)
		)
	}.play
)
このような「LocalIn」と「LocalOut」のペアによってディレイのフィードバックを構成する方法の基本的な制限は、defaultで64サンプルである「ブロックサイズ」にある。 「ControlDur.ir」でディレイの時間を設定しているのは、最大周波数が「SampleRate.ir/ControlDur.ir」(標準的には44100/64 = about 690Hz)となるからである。 これを改変したサンプルとして、以下ではディレイラインの長さを変調することでビブラートを実現している。 こんな音がした。

(
	{
		var freq,time, ex, delay, filter, local;
		freq= 440;
		time= freq.reciprocal;
		ex= WhiteNoise.ar(
			EnvGen.kr(
				Env([1.0,1.0,0.0,0.0], [time,0,100])
			)
		);
		freq= SinOsc.ar(6, 0, 10, freq);
		time= freq.reciprocal;  
		local= LocalIn.ar(1);
		filter= LPZ1.ar(ex+local); //apply filter
			//maximum delay time is 440-10
		delay= DelayN.ar(
			filter, 
			430.reciprocal, 
			time-ControlDur.ir
		);  
		LocalOut.ar(delay*0.99); 
		Out.ar(0, Pan2.ar(filter,0.0))
	}.play
)

ここからは、「Thor Magnusson」氏の協力によって新たに整理された例を見ていこう。 まず、以下は瞬間的なバーストを作り出すノイズUGenである。 こんな音がした。


(
	{  
		var burstEnv, att = 0, dec = 0.001;
			//Variable declarations 
		burstEnv = EnvGen.kr(Env.perc(att, dec), gate: Impulse.kr(1));
			//envelope 
		PinkNoise.ar(burstEnv);
			//Noise, amp controlled by burstEnv 
	}.play
)

このノイズを楽音にするためにディレイラインによる櫛型フィルタをかける。 そこで、以下のようなKarplus-StrongのSynthDefを定義する。


SynthDef(
	\ks_guitar, 
		{
			arg note, pan, rand, delayTime, noiseType=1;
			var x, y, env;
			env = Env.new(#[1, 1, 0],#[2, 0.001]);
				// A simple exciter x, with some randomness.
			x = Decay.ar(
				Impulse.ar(0, 0, rand), 
				0.1+rand, 
				WhiteNoise.ar
			); 
			x = CombL.ar(
				x, 
				0.05, 
				note.reciprocal, 
				delayTime, 
				EnvGen.ar(env, doneAction:2)
			); 
			x = Pan2.ar(x, pan);
			Out.ar(0, LeakDC.ar(x));
		}
).store;
以下は、このSynthDefを20音、ランダム性を加えて演奏させてみるサンプルである。 こんな音がした。

(
	{
		20.do(
			{
				Synth(
					\ks_guitar, 
						[
							\note, 220+(400.rand), 
							\pan, 1.0.rand2, 
							\rand, 0.1+0.1.rand, 
							\delayTime, 2+1.0.rand
						]
				);
				(1.0.rand + 0.5).wait;
			}
		);
	}.fork
)

以下は、パターンを自動演奏させてみたサンプルである。 こんな音がした。


a = Pdef(
	\kspattern, 
	Pbind(
		\instrument, 
		\ks_guitar, 
		\note, Pseq.new([60, 61, 63, 66], inf).midicps,
		\dur, Pseq.new([0.25, 0.5, 0.25, 1], inf),
		\rand, Prand.new([0.2, 0.15, 0.15, 0.11], inf), 
		\pan, 2.0.rand-1,
		\delayTime, 2+1.0.rand; 
	)
).play;

・・・ここで、夕食から晩のライヴへと出かけた。 これは、そのライブ5分前のサウンドチェックの模様である。 詳しいことはまた、帰国後に講義など、どこかで紹介することにして、今日のSuperColliderはここまでである。

2011年9月2日(金)

アルスエレクトロニカ探訪2日目となるこの日は、朝から霧である。 日本では台風12号が本土直撃の模様で、CNNのニュースでもやっていた。 昨夜のライブはTesla Orchestraというパフォーマンスで、テスラコイルで作った100万ボルトの放電をサウンドに変換する、というシステムであるが、これは日本でもやっている大学があり、それほど珍しくない。 しかし、20キログラムもある金属製の防護服を作って、人間のパフォーマーが避雷針になる、という発想が凄い(^_^;)。 1曲だけ、Webに公演の模様を上げてみた。 これ である。

さて、物理モデル合成の、「Thor Magnusson」氏の協力によって新たに整理された例の続きである。 まずは、「Karplus-Strong」モデルのエキサイターのノイズをホワイトノイズとピンクノイズとで比較するという。 ホワイトノイズとは全ての周波数成分を均一のランダムで含むノイズ、ピンクノイズは周波数成分に偏りをもたせたノイズである。 以下はホワイトノイズを用いたもので、 こんな音がした。


(
	{  
		var burstEnv, att = 0, dec = 0.001; 
		var burst, delayTime, delayDecay = 0.5; 
		var midiPitch = 69; // A 440 
		delayTime = midiPitch.midicps.reciprocal; 
		burstEnv = EnvGen.kr(Env.perc(att, dec), gate: Impulse.kr(1/delayDecay));  
		burst = WhiteNoise.ar(burstEnv);  
		CombL.ar(burst, delayTime, delayTime, delayDecay, add: burst);  
	}.play
) 

以下はピンクノイズを用いたもので、 こんな音がした。 ごく僅かな違いのようだが、ここに拘らなくては物理モデルのオタクにはなれない。


(
	{  
		var burstEnv, att = 0, dec = 0.001; 
		var burst, delayTime, delayDecay = 0.5; 
		var midiPitch = 69; // A 440 
		delayTime = midiPitch.midicps.reciprocal; 
		burstEnv = EnvGen.kr(Env.perc(att, dec), gate: Impulse.kr(1/delayDecay));  
		burst = PinkNoise.ar(burstEnv);  
		CombL.ar(burst, delayTime, delayTime, delayDecay, add: burst);  
	}.play
) 

パラメータのうち「delayTime」は生成するサウンドのピッチを制御する。 毎秒100回(1/100秒)のディレイ(抑制する周期点)は100Hzのサウンドを、毎秒400回(1/400)は400Hzのサウンドを生成する。 以下の例ではmidiノートとしてピッチを与えるSyntheDefを定義して、midiシーケンサのようにメロディーを与えて呼び出して自動演奏している。 こんな音がした。 見事な自動演奏であり、人間の知覚認知が、12等分平均律の体系と等間隔の時間単位によるビートに基づいていることをあらためて実感できる。


(
	SynthDef(
		\KSpluck, 
			{ 
				arg midiPitch = 69, delayDecay = 1.0;
				var burstEnv, att = 0, dec = 0.001;
				var signalOut, delayTime;
				delayTime = [midiPitch, midiPitch + 12].midicps.reciprocal;
				burstEnv = EnvGen.kr(Env.perc(att, dec)); 
				signalOut = PinkNoise.ar(burstEnv); 
				signalOut = CombL.ar(
					signalOut, 
					delayTime, 
					delayTime, 
					delayDecay, 
					add: signalOut
				); 
				DetectSilence.ar(signalOut, doneAction:2);
				Out.ar(0, signalOut)
			}
	).store;
)

(
		//Then run this playback task
	r = Task(
		{
			{
				Synth(
					\KSpluck, 
					[
						\midiPitch, rrand(30, 90), //Choose a pitch
						\delayDecay, rrand(0.1, 3.0) //Choose duration
					]
				);
					//Choose a wait time before next event
				[0.125, 0.125, 0.25].choose.wait;
			}.loop;
		}
	).play
)

ここではさらに、もっと面白いものがあるぞ、と以下のような物理モデル音源に使える要素が提示されている。 それぞれ、リファレンスをplain textとしてリンクしたので参照されたい。

ここからさらに、[Spring]と[Ball]と[TBall]という、3種類の物理モデル楽音合成のサンプルが、名前だけ紹介されている。 ちょっと鳴らしてみるとなかなか面白い音がするので、順になぞっていくことにした。 まずは「Spring」(physical model of resonating spring)である。 これはUGenを上位クラスに持ち、以下のように定義されている。

*ar(in, spring, damp)
*kr(in, spring, damp)

	in		modulated input force
	spring	spring constant (incl. mass)
	damp	damping
以下は「Spring」の最初のサンプルで、 こんな音がした。

	// trigger gate is mouse button
	// spring constant is mouse x
	// mouse y controls damping
(
	{ 
		var inforce, outforce, freq, k, d;
		inforce = K2A.ar(MouseButton.kr(0,1,0)) > 0; 
		k = MouseY.kr(0.1, 20, 1);
		d = MouseX.kr(0.00001, 0.1, 1);
		outforce = Spring.ar(inforce, k, d);
		freq = outforce * 400 + 500; 
			// modulate frequency with the force
		SinOsc.ar(freq, 0, 0.2)
	}.play;
)

以下は「Spring」の2番目のサンプルで、 こんな音がした。


	// several springs in series.
	// trigger gate is mouse button
	// spring constant is mouse x
	// mouse y controls damping
(
	{ 	var m0, m1, m2, m3, d, k, inforce;
		d = MouseY.kr(0.00001, 0.01, 1); 
		k = MouseX.kr(0.1, 20, 1);
		inforce = K2A.ar(MouseButton.kr(0,1,0)) > 0; 
		m0 = Spring.ar(inforce, k, 0.01);
		m1 = Spring.ar(m0, 0.5 * k, d);
		m2 = Spring.ar(m0, 0.6 * k + 0.2, d);
		m3 = Spring.ar(m1 - m2, 0.4, d);
		SinOsc.ar(m3 * 200 + 500, 0, 0.2) 
			// modulate frequency with the force
	}.play;
)

以下は「Spring」の最後のサンプルで、 こんな音がした。


	// modulating a resonating string with the force
	// spring constant is mouse x
	// mouse y controls damping 
(
	{ 	var m0, m1, m2, m3, m4, d, k, t;
		k = MouseX.kr(0.5, 100, 1);
		d = MouseY.kr(0.0001, 0.01, 1);
		t = Dust.ar(2);
		m0 = Spring.ar(ToggleFF.ar(t), 1 * k, 0.01);
		m1 = Spring.ar(m0, 0.5 * k, d);
		m2 = Spring.ar(m0, 0.6 * k, d);
		m3 = Spring.ar([m1,m2], 0.4 * k, d);
		m4 = Spring.ar(m3 - m1 + m2, 0.1 * k, d);
		CombL.ar(t, 0.1, LinLin.ar(m4, -10, 10, 1/8000, 1/100), 12)		
	}.play;
)

次は、「Ball」(physical model of bouncing object)である。 これはUGenを上位クラスに持ち、以下のように定義されている。


*ar(in, g, damp, friction)
*kr(in, g, damp, friction)

	in		modulated surface level
	g		gravity
	damp	damping on impact
	friction	proximity from which on attraction to surface starts
以下は「Ball」の最初のサンプルで、 こんな音がした。

	// mouse x controls switch of level
(
	{ 
		var f, sf;
		sf = K2A.ar(MouseX.kr > 0.5) > 0;
		f = Ball.ar(sf, MouseY.kr(0.01, 20, 1), 0.01);
		f = f * 10 + 500;
		SinOsc.ar(f, 0, 0.2)
	}.play;
)

以下は「Ball」の2番目のサンプルで、 こんな音がした。


	// mouse x controls modulation rate
	// mouse y controls gravity
(
	{ 
		var f, sf, g;
		sf = LFNoise0.ar(MouseX.kr(1, 100, 1));
		g = MouseY.kr(0.1, 10, 1);
		f = Ball.ar(sf, g, 0.01, 0.01);
		f = f * 140 + 500;
		SinOsc.ar(f, 0, 0.2)
	}.play;
)

以下は「Ball」の最後のサンプルで、 こんな音がした。 どのあたりがドイツのパトカーのサイレンなのかは不明である。(^_^;)


	// the general german police choir
	// mouse x controls damping
	// mouse y controls gravity
(
	{ 
		var f, sf, g;
		sf = LFPulse.ar(0.6, 0.2, 0.5);
		g = MouseY.kr(0.1, 10, 1);
		d = MouseX.kr(0.0001, 1, 1);
		f = Ball.ar(sf, g, d);
		f = f * 140 + 400;
		SinOsc.ar(f, 0, 0.2)
	}.play;
)

最後は、「TBall」(physical model of bouncing object)である。 バウンドする物体が、振動する表面に衝突して振動する、という物理モデルである。 これはUGenを上位クラスに持ち、以下のように定義されている。


*ar(in, g, damp, friction)
*kr(in, g, damp, friction)

	in			modulated surface level
	g			gravity
	damp		damping on impact
	friction		proximity from which on attraction to surface starts
以下は「TBall」の最初のサンプルで、 こんな音がした。

	// mouse x controls switch of level
	// mouse y controls gravity
(
	{ 
		var t, sf;
		sf = K2A.ar(MouseX.kr > 0.5) > 0;
		t = TBall.ar(sf, MouseY.kr(0.01, 1.0, 1), 0.01);
		Pan2.ar(Ringz.ar(t * 10, 1200, 0.1), MouseX.kr(-1,1)); 
	}.play;
)

以下は「TBall」の2番目のサンプルで、 こんな音がした。


	// mouse x controls step noise modulation rate
	// mouse y controls gravity
(
	{ 
		var t, sf, g;
		sf = LFNoise0.ar(MouseX.kr(0.5, 100, 1));
		g = MouseY.kr(0.01, 10, 1);
		t = TBall.ar(sf, g, 0.01, 0.002);
		Ringz.ar(t * 4, [600, 645], 0.3); 
	}.play;
)

以下は「TBall」の3番目のサンプルで、 こんな音がした。


	// mouse x controls sine modulation rate
	// mouse y controls friction
	// gravity changes slowly
(
	{ 
		var f, g, h, fr;
		fr = MouseX.kr(1, 1000, 1);
		h = MouseY.kr(0.0001, 0.001, 1);
		g = LFNoise1.kr(0.1, 3, 5);
		f = TBall.ar(SinOsc.ar(fr), g, 0.1, h);
		Pan2.ar(Ringz.ar(f, 1400, 0.04),0,5)
	}.play;
)

以下は「TBall」の4番目のサンプルで、 こんな音がした。


	// sine frequency rate is modulated with a slow sine
	// mouse y controls friction
	// mouse x controls gravity
(
	{ 
		var f, g, h, fr;
		fr = LinExp.kr(SinOsc.kr(0.1), -1, 1, 1, 600);
		h = MouseY.kr(0.0001, 0.001, 1);
		g = MouseX.kr(1, 10);
		f = TBall.ar(SinOsc.ar(fr), g, 0.1, h);
		Pan2.ar(Ringz.ar(f, 1400, 0.04),0,5)
	}.play;
)

以下は「TBall」の最後のサンプルで、 こんな音がした。 マウスでぐりぐりするのが楽しくなる。(^_^)


	// this is no mbira: vibrations of a bank of resonators that are 
	// triggered by some bouncing things that bounce one on each resonator	
	// mouse y controls friction
	// mouse x controls gravity
(
	{ 
		var sc, g, d, z, lfo, rate;
		g = MouseX.kr(0.01, 100, 1);
		d = MouseY.kr(0.00001, 0.2);
		sc = #[451, 495.5, 595, 676, 734.5]; 
			//azande harp tuning by B. Guinahui
		lfo = LFNoise1.kr(1, 0.005, 1);
		rate = 2.4;
		rate = rate * sc.size.reciprocal;
		z = sc.collect 
			{ 
				|u,i|
				var f, in;
				in = Decay.ar(
					Mix(
						Impulse.ar(
							rate, 
							[1.0, LFNoise0.kr(rate / 12)].rand, 
							0.1
						)
					), 0.001
				);
				in = Ringz.ar(
					in, 
					Array.fill(4, { |i| (i+1) + 0.1.rand2 }) / 2
						* Decay.ar(in,0.02,rand(0.5,1), lfo) * u, 
						Array.exprand(4, 0.2, 1).sort
					);
				in = Mix(in);
				f = TBall.ar(in * 10, g, d, 0.001);
				in + Mix(
					Ringz.ar(
						f, 
						u * Array.fill(
							4, 
							{ |i| (i+1) + 0.3.rand2 }
						) * 2, 
						0.1
					)
				)
			};
		Splay.ar(z) * 0.8
	}.play;
)

・・・ここで時間となったので、2日目のアルスに出かけた。 まずはアルスエレクトロニカセンターに行って、全ての展示をチェックした。 去年も行っているので、場所が変わっても同じ常設展示が多かったが、素晴らしい作品にも出会った。 次にハウプト広場に戻って、今年の「キャンパス」の筑波大学を見たが、 「ビーコン」を展示している内山先生には、外出中ということで再会できなかった。 そして午後と晩の2回、Mariendomという教会に行った。 これはリンツにありながら、なんとオーストリア国内で最大規模の教会ということである。

まず午後には、螺旋階段で登った小部屋で、アンドロイドと人間による「劇」を見た。 阪大の石黒先生のアンドロイドは、手足の無い子供みたいなもの、アザラシ型、石黒先生本人のもの、 などが全てアルスセンターで展示されていたが、 今回の「人間の役者とアンドロイドでの劇」という企画には、やはりちょっと無理があったようだ。 せっかくの凛とした美女なのに、顔面神経痛のように、まばたきのたびに眉間に皺が寄るのがどうにも気になって仕方なかった。 人間のまばたきの自然さと表現力をあらためて確認した。 また、あらかじめ録音した「声」は背後のスピーカから出ていて、せっかくリップシンクで唇が動いているものの、 生身の人間の役者との「会話劇」になっていなかった。 「声は身体から出てくる」という事実もまた、アンドロイドの永遠の課題なのだった。 薄暗い教会の部屋と効果的なスポット照明と豊富な残響に相当に助けられていてコレであり、 現在のアンドロイドの限界を明確に知ることが出来た、という意味では収穫だったかもしれない。

そして晩に再びMariendomに出かけたが、「100000立方メートル」というこの空間を使った空間音響インスタレーションとパフォーマンス、 という企画である。晩の8時54分から始まって、終わるのが翌朝の5時13分ということであるが、もちろん冒頭部分だけのつもりである。 ・・・結果としては予想通り、「会場に負けた(^_^;)」という、まぁ普通の空間音響モノであった。 ICMCとNIMEで優れた空間音響作品に触れてきた立場からすれば、残念ながらサウンドもとりたてて新しいものではなかった。

2011年9月3日(土)

さて、アルス3日目である。晩にはドナウパークで花火ショーであるが、Yahoo.comの天気予報によれば今夜も天気はまずまずである。 どうしても早朝に目覚めるので、出かける前にまたSuperColliderである。 物理モデル楽音合成の続きであるが、次に登場するのは、 SuperColliderの「STK Library」にあるという、「MdaPiano」(Piano synthesiser)である。 よくあるビアノのPCMサンブリング音でなく、物理モデルとしてまっとうにピアノ音を生成してくれるスグレモノである。 これは元々はPaul Kellett氏によってVSTブラグインとして作られたものをDan Stowell氏がSuperColliderに移植したという。 このUGenはポリフォニックではないが、音列としてリトリガする事は出来る。 定義は以下であるが、このUGenはステレオであり、"wideness"パラメータで制御できるようになっている。

MdaPiano.ar(freq, gate, ... )

	freq - Frequency of the note
	gate - note-on occurs when gate goes from nonpositive to positive; 
		note-off occurs when it goes from positive to nonpositive. 
		Most of the other controls are only updated when a new note-on occurs.
	vel - velocity (range is 0 to 127, default 100)
	decay - The time for notes to decay after the initial strike (default 0.8)
	release - The time for notes to decay after the key is released (default 0.8)
	hard - 
	velhard - 
	muffle - 
	velmuff - 
	velcurve - 
	stereo - Width of the stereo effect (which makes low notes sound 
			towards the left, high notes towards the right). 0 to 1.
	tune - Overall tuning
	random - Randomness in note tuning (default 0.1)
	stretch - Stretches the tuning out (higher notes pushed higher)
	sustain - If positive, act as if the piano's sustain pedal is pressed.
まずは以下のように、1秒ごとにランダムに単音をトリガした例である。 とても物理モデルとは思えない、いい音である(^_^)。 こんな音がした。 メモリを喰うので、最後にメモリを解放するのも重要である。

s.boot;

(
	x = {
		MdaPiano.ar(
			LFNoise0.kr(1).range(20, 60).round.midicps,
				// random note
			stereo: 0.5, 
			gate: LFPulse.kr(1), 
			vel: LFPar.kr(0.1).range(30, 100), 
				// varying velocity
			mul: 0.2
		)
	}.play
)

x.free;

以下は、この「MdaPiano」をSynthDefとして定義して、自動演奏させているサンプルである。 こんな音がした。


(
	SynthDef(
		\help_mdapiano, 
			{ 
				|out=0, freq=440, gate=1|
				var son = MdaPiano.ar(
					freq, 
					gate, 
					release: 0.9, 
					stereo: 0.3, 
					sustain: 0
				);
				DetectSilence.ar(
					son, 
					0.01, 
					doneAction:2
				);
				Out.ar(
					out, 
					son * 0.1
				);
			}
	).memStore;
)

// Then we can use it in a pattern:
(
	TempoClock.default.tempo = 1.2;
	Ppar(
		[
			Pbind(
				\instrument, 
				\help_mdapiano,
				\degree, Pseq(
					[ 
						0, 7, -5, 7,  0, 5, -7, 5,  -2, 5, -7, 5,  -2,  3, -9,  3, 
						0, 7, -5, 7,  0, 5, -7, 5,  -2, 5, -7, 5,  -2, -3, -4, -5 
					], 
					inf
				),
				\dur, 0.5, 
				\octave, 3, 
				\root, 3,
				\vel, Prand(
					[
						Pseq(
							[100, 30, 50, 10]
						), 
						Pseq(
							[100, 30, 10, 10, 5, 10, 20, 30]
						)
					], 
					inf
				),
				\legato, 0.95
			),
			Pbind(
				\instrument, 
				\help_mdapiano,
				\degree, Pseq(
					[ 
						\, 0, -1, 0, 2, 0, \, \, \, 0, -2, \, \, -2, -4, \, \ 
					], 
					inf
				),
				\dur, 0.5, 
				\octave, 6, 
				\root, 3,
				\vel, Pwhite(50, 100, inf)
			)
		], 
		inf
	).play
)

次は、MembraneUGensというグループで、「膜」状の物体を叩いて振動させる、という物理モデルである。 ドラムを叩いて、その皮の振動をきちんと物理モデルしている。 以下は、まさにドラムのイメージの円形の膜の振動「MembraneCircle.ar(excitation, tension, loss)」のサンプルであり、 マウスのX・Y座標によって色々な音がする。 マウスクリックによって「叩いた」後でマウスカーソルを移動させれば、ちゃんと鳴りつつサウンドが連続的に変化するのが、PCMサンブリングなどとはまったく違うところである。 こんな音がした。


(
	{ 
		var excitation = EnvGen.kr(
			Env.perc, 
			MouseButton.kr(0, 1), 
			timeScale: 0.1, 
			doneAction: 0
		) * PinkNoise.ar(0.4);
		var tension = MouseX.kr(0.01, 0.1);
		var loss = MouseY.kr(0.999999, 0.999, 1);
		MembraneCircle.ar(
			excitation, 
			tension, 
			loss
		);
	}.play;
)

次に名前だけ紹介されているのは、以下のように定義された「TwoTube」(physical modeling simulation; two tubes)である。


TwoTube.ar(input=0, k=0.01, loss=1.0, d1length=100,d2length=100,  mul = 1.0, add = 0.0)

	Physical model; two tube sections with scattering junction 
	inbetween; their relative areas determine k. 

	input-		Excitation to inject into the system
	k-		Scattering coefficient for junction of two tubes, usually -1<=k<=1
	loss-		Amplitude loss factor in circulation
	d1length-	Length in samples of first delay line
	d2length-	Length in samples of second delay line 
			(no interpolation implemented yet)
2本の「棒」がぶつかるのを物理モデリングする、というものらしい。 こんなものが面白いのか、と思うのは素人で、現実に存在する「棒」から出てくるサウンドに「似せる」だけでなく、 そのパラメータを色々に拡張してやれば、地球上に、あるいは宇宙のどこにも存在しない「棒」から出てくる新規なサウンドも作れるのである。 以下の例では、 こんな音がした。

(
	{
		var delay1, delay2, source; 
			//k from -1 to 1
			//in samples
		delay1= 100; 
		delay2= 40;
		source= WhiteNoise.ar(0.5)*EnvGen.ar(
			Env(
				[1,1,0],
				[(delay1+delay2)/SampleRate.ir,0.0]
			), 
			Impulse.kr(MouseY.kr(1,4))
		);
		TwoTube.ar(source,MouseX.kr(-1,1),0.99,delay1,delay2); 
	}.play
)

以下の例では、この「TwoTube」をSynthDefで定義して、簡単な自動演奏をさせている。 こんな音がした。


(
	SynthDef(
		\twotube,
			{
				arg delay1=100, delay2=50, k=0.0, loss=0.999, dur=0.5, pan=0.0; 
				var source; 
					//k from -1 to 1
				source= WhiteNoise.ar(0.5)*EnvGen.ar(
					Env(
						[1,1,0,0],
						[(delay1+delay2)/SampleRate.ir,0.0,1.0]
					)
				);
				Out.ar(
					0,
					Pan2.ar(
						TwoTube.ar(source,k,loss,delay1,delay2)
							*EnvGen.ar(
								Env(
									[0,1,1,0],
									[0.001]++((dur-0.001)*[0.4,0.6])
								),
								doneAction:2
							),
						pan
					)
				); 
			}
	).send(s); 
)

(
	t.stop;
	t={
		inf.do{
			Synth(
				\twotube,
				[
					\delay1, rrand(1,300),
					\delay2, rrand(1,300),
					\loss, rrand(0.9,0.999),
					\dur, rrand(0.1,5.0), 
					\pan, rrand(-1,1.0), 
					\k, rrand(-1,1.0)
				]
			);
			0.5.wait;
		};
	}.fork;
)

「TwoTube」に続いて、「NTube」という名前も紹介されている。たぶん2本でなく「n本の棒」だと想像できるが、ここは省略する。 これらは「SLUGens」という中にあるのだ、ということで、 ここ が紹介されている。 リンク先から「sc3-plugins_32_bit.tar.gz」をダウンロードして解凍すると、 以下のような 膨大なライブラリ があった。

この中のたった一つが「SLUGens」であり、その中だけで、以下のものが含まれている。 まさに膨大な努力の賜物であり、物理モデルの研究者の執念を感じることができる(^_^)。

この他にも、「great fun!」だという、 PMSC Library が紹介されている。 これらが全てフリーである。いくらでも探検できる秘境だと言えるだろう。

このセクションの最後には、Perry CookとGary Scavoneによって作られた物理モデルのSTKキットを、Paul LanskyがSuperColliderに移植したという「STKライブラリ」の紹介と、その例としてマンドリンのサンプルソースがあったが、これを鳴らすにはけっこう面倒なインストール作業が必要なので、ここでは省略する。 これでようやく、物理モデルのセクションが終わりである。(^_^)

この日は、インタラクティブアートの展示を行っている「OKセンター」に行き、さらにアルスエレクトロニカと併設でOK センターが行っている展示企画なども見て回った。 圧巻だったのは、展示を見つつ順路に従って進んでいくと、なんといつの間にか隣の建物に入り、 さらにその隣の教会の屋根裏部屋に入って行ったこと。 展示の順路のために、新たに壁に穴をあけて順路を建築したらしい。 そしてなんと、屋根裏部屋のインスタ展示会場から階段を降りていくと、表通りに面した教会の立派な聖堂に出た。 そして、どこかで聴いたような曲が・・・と思ったら、タリスのあの40声のモテット( 楽譜はこれ )だった。

聖堂の参列者席の周囲には、ぐるりと40個の立派なスピーカーが林立していた。 もちろん1グループ5個で、客席の前後左右とナナメ対角線の8方向、で計40個である。 そして流れてくる40声の演奏は、スピーカ1個ごとにちゃんと録音されていた。つまり、正に「40チャンネル・ステレオ」で、タリスの楽譜の通りに再現していたのである。 これは偶然とはいえ、素晴らしい再会となった。

そして晩には、ドナウ河畔に数万人が集まる、光と音のスペクタクルがある。 花火とレーザーと野外大音量で、この晩のリンツは全域が深夜まで騒々しい。 ちょっとホテルで休憩して、防寒の用意をして、出かけることになるので、今日はここまでである。

2011年9月4日(日)

アルス4日目、日曜日となった。 部屋のテレビのCNNニュース(日本の首相が替わった話題などは全くスルー)にもなっているほどで、 台風12号は相当な被害となっているらしい。 この台風が生まれたのは日本を出発する3日ほど前だったが、前線に阻まれてだいぶグズグズ停滞していて、 出国時点ではまだ遠くにあり問題なかった。 そしてどうやら、日本海に抜けてさらに北上して熱帯低気圧になっていずれ消えた頃に帰国、 ということで、こちらも問題ない。台風12号を避けるように渡欧した形である。 出来たばかりの台風13号も東の方なので関係なさそうだ。 もっとも、明日の午後にリンツ出発であるが、明日のリンツの天気予報は「激しい雨」とある(^_^;)。

出発する頃にちょっと喉のあたりに違和感があったのは、「イソジン」と「のどぬーる」と「龍角散のど飴」と「葛根湯」と「マルチビタミン」で なんとか制圧したものの、ここらにきてやや疲れてきた。 まぁ、原因は「毎日飲み過ぎ」とハッキリしているのだが(^_^;)、この日はLentosミュージアムとブルックナーハウスの展示をチェックし、 あとはドナウパークのベンチでのんびりしたりして、日曜日らしい休養日となった。 今は午後にいったんホテルに戻ってパソコンを開いたが、 晩にはLentosミュージアムとドナウパークとブルックナーハウスとで、色々な「ヘンな楽器」シリーズのパフォーマンスがある模様なので、 最後の晩の楽しみとして出かける予定である。

さて、SuperColliderである。 物理モデルのトピックが終わったので、 Workshop materials for Computer Music (G6002)12. SC Programming 2 and Extensions12.1 Dealing with Large Projects.html からという事になる。 あまり関係しそうにないが、SuperColliderのコードがどんどん長大になっていった場合に、どこかで複数のファイルに分けて全体のプロジェクトを扱う、という話題である。 これはMax5などでも持っている機能であるが、パソコンのメモリの増加の方が進んでいるので、 過去の非力なパソコンの時代のような分割化(階層化・構造化とはやや違う)は、ちょっと時代遅れかもしれない。 一気に扱えるのであれば、切り替えの無駄が無いので、その方がいいのである。

現在のSuperCollider環境で、他のSuperColliderブログラムを記述したファイルを取り込む方法は以下である。 実際にやってみるのが判りやすい。 まず、


{SinOsc.ar(440,0,0.1)}.play
というSuperColliderプログラム(postウインドウで実際に走ることを確認したものをコピペでよい)を、 「extrafile.rtf」というファイル名で保存する。 リットテキストなど普段は使わないが仕方ない(^_^;)。 これを、以下のように、アプリケーションのSuperColliderのあるディレクトリに置く。

これであとは、以下のように「"extrafile.rtf".loadPath;」と実行させると、SuperColliderプログラムファイルが読み込まれて実行される。 音は省略である。(^_^;)


"extrafile.rtf".loadPath; //after creating extrafile.rtf

以下のように記述しても、同様に実行される。 こちらの方が、 このように Unixペースの膨大なファイル処理に対応しているので、便利でお奨めだという。


thisProcess.interpreter.executeFile("extrafile.rtf");

もちろん、SuperColliderのプログラムが長大になっていった場合の対応策としては、 ここで紹介した分割ファイル化というのは本質的ではない。 オブジェクト指向のクラスを使って、カプセル化(抽象化)して、コードをソフトウェア部品として再利用する、というのが本命である。 ・・・というところで、最初のイントロのセクションは、アッサリと終わりである。

本命はこの次の、 12.2 Writing Classes.html の 「Using and Writing Classes in SuperCollider」 である。 どうやら、オブジェクト指向のSuperColliderについて、きちんと本格的にオブジェクト指向の解説とともに整理している模様で、 かなり長大っぽい(^_^;)。 ここでは、 NastySynth.sc と、 SuperMario.sc という2本のSuperColliderプログラムが必要だという。 スーパーマリオとは、ちょっと期待させてくれる。(^_^)

2011年9月5日(月)

アルス5日目、夕方には帰国に出発という日は、天気予報の通りに、朝から雨模様である。 チェックアウトまでのホテルの部屋、落ち穂拾いのアルスセンター、空港や機内などで、ぼちぼち進めていくことにしよう。 前夜のLentosとブルックナーハウスでのコンサートは、色々と考えさせられた。 Lentosミュージアムの吹き抜け部分には、KAWAIが協力して、7台のアップライトピアノにオリジナルの自動演奏機構を取り付けて、 OSC経由でPureData(Maxみたいなもの)から演奏情報を送る(円周上に配置された7台のピアノが、 呼応したり強調したりして、ピークではほぼ全てのピアノのほぼ全ての88鍵盤を激しく連続打鍵 -- 人間には不可能)、 というパフォーマンスがあった。 システムとしては10年どころか15年以上前からあるもので、作曲的にもICMC的にはまったく目新しいことは無かったが、 アルスの来場者にはそこそこ新鮮だったようである。

問題は、ブルックナーハウスでの「オケ+映像」という作品のコンサートであった。 結論としては、「オケと映像は合わない」という、当たり前の感想となった。 作品2曲は、武満とタンドゥンを足して5で割ったぐらいのそこそこ良い現代音楽で、 ブルックナー交響楽団の演奏も素晴らしく、指揮者も岩城裕之とコバケンを足して3で割ったぐらいの優秀な演奏であった。 しかし、だからなおさら、ステージ後方の大スクリーンにライブ投射される映像がひたすら邪魔だった。 片方は「白と青」、もう片方はモノクロの、実写もカラーも無い、抽象的な数理造形的映像であったが、それでも邪魔だった。 録音された現代曲に映像を同期させて付けるという「ミュージックビデオ」や「映像音響詩」や「モーショングラフィクス」というのはOKとしても、 ポップ/ロック的なビートのない現代音楽(先が読めないところが魅力)に、iTunesのようにライブ音響に反応しているわけでもない、 ちゃんと同期していないライプ映像を付加することにはあまり意味が無いのだ、と実感した。

さて、SuperColliderの方は、 Workshop materials for Computer Music (G6002)12. SC Programming 2 and Extensions12.2 Writing Classes.html からである。 「Using and Writing Classes in SuperCollider」ということで、以下のように、いつものinternalサーバを起動する。


Server.default=s=Server.internal
最初のトピックはいきなり大上段に「What is Object Oriented Programming?」である。 ゼリーを「型」に入れてたくさん作る、という状況を考えてみよう。 型はたった1個でも、何度も作る時間とゼリーの材料が十分にあれば、たくさんの同じ形のゼリーを作れる。 ゼリーを作る時間はけっこうかかるが、パソコンの膨大なメモリと高速処理を考えると、 SuperColliderなどのオブジェクト指向システムにとって、 1個のクラス(型)から、一瞬にしてほぼ無尽蔵なオブジェクトやインスタンス(ゼリー)を生成することが出来る。

クラス(class)というのは、あるカテゴリのプロパティ(特性)を概要としてまとめたもの、である。 例えば「キリン」というクラスを作ろうとした場合には、色々な概念から選んでキリンの特性を記述する。 「首が長い」「4本の長い足を持つ」「体表に毛に覆われている/いない、の模様がある」などのクラスをいくつも記述し、 このクラスを用いて実際の「キリン」オブジェクトをいくつも生成できる。 個々のキリンは、首の長さ、足の長さ、体表の模様、旅行者を襲うかどうか、などのプロバティがそれぞれ異なるものの、 クラスとしての「キリン」から生成できるところがポイントである。 「Object Oriented Programming」については、これだけでなくもう少しあるが、それをぼちぼち解明していこう。

SuperColliderは「Object Oriented Programming Language,」である。 そしてオブジェクト指向言語でのお約束として、JavaとかAcrtionScriptと同様に、以下のように、用語の先頭には大文字を使う。

SuperColliderには、色々なタイプのオーディオのオブジェクトを記述するクラスがある。 レシピからオブジェクトを作ろうとした時、サイン波のオシレータ、低周波ののこぎり波オシレータ、あるいはローパスフィルタ、 といった色々なインスタンスを使うことができる。 すでにたくさんのサイン波オシレータがあるが、ここではあらためて「SinOsc class」から作ってみよう。 以下の例では、「ar」(オーディオレート[サンプリング44.1kHz])のクラスメソッドによって3つのSinOscオブジェクトを作り、 ミックスし、鳴らして(play)いる。 こんな音がした。 ちなみに、周波数の比が「4:5:6」となっているので、鳴らす前から「純正な長3和音」と判る。

{
	Mix.new(
		[
			SinOsc.ar(440),
			SinOsc.ar(550),
			SinOsc.ar(660)
		]
	)*0.3
}.play

上の例は純正律の長3和音であったが、ここで比較するために、「12等分平均律」の長3和音も作ってみた。 平均律の半音は「2の(1/12)乗」であるから、長3度にはこれを4乗して、完全5度にはこれを7乗してやればいい。 見事に濁った、 こんな音がした。 こんな濁ったピッチにチューニングされたピアノを小さい時から弾いて(聞いて)いては、 純正なハーモニーのアカペラの良さを知る感覚はとうてい育たない。 合唱の世界では、ピアノ経験者ほどハーモニー音感が悪い。(^_^;)


{
	Mix.new(
		[
			SinOsc.ar(440),
			SinOsc.ar(440*pow(pow(2,(1/12)),4)),
			SinOsc.ar(440*pow(pow(2,(1/12)),7)),
		]
	)*0.3
}.play

このように、SinOscクラスを作る方法は、クラス「ar」のクラスのクラスメソッドを利用する、というものである。 クラスメソッドというのは単純に「関数(機能)」であるが、クラスに結びつけられた関数である。 このようなクラスメソッドが、オブジェクトを作るためのレシピとなる。 そのもっとも一般的なのが「new」メソッドである。

以下は、新しいウインドウオブジェクトを作る例である。 上にある1行は、下にある2行をまとめた書式である。


w=SCWindow.new("new window object", Rect.new(100,100,400,400));
	//which is used so commonly in SC, that there 
	//is a special shortcut of missing it out:

w= SCWindow("new window object", Rect.new(100,100,400,400));
w= SCWindow(); 
	//accept defaults
このように「new」クラスメソッドで生成されたオブジェクトに対して、インスタンスメソッドを与えることで、そのオブジェクトが反応する。 以下の例では、上の例で生成されたウインドウオブジェクト「w」(メモリ内に定義し生成されただけ)に対して、 インスタンスメソッド「front」を与えることで、実体としてスクリーン上にウインドウが「現れる」。

w=SCWindow.new("new window object", Rect.new(100,100,400,400));

w.front; 
	//make window appear by calling an instance 
	//method on the instance (object) we just made

以下の例では、上の例のオブジェクトウインドウ「w」に対して、「view」オブジェクトにインスタンスメソッド「backColor_」を呼び出して、 ウインドウの内部に色をつけている。


w=SCWindow.new("new window object", Rect.new(100,100,400,400));

w.front; 

w.view.backColor_(rgb(255,0,45));
	//call an instance method (backColor_) on the 
	//view object belonging to the window object

任意のクラスに関して、どのようなクラスメソッドやインスタンスメソッドがあるのか、を調べる方法は以下である。

同様にして、「backColor_」に「cmd+Y」で以下が出た。

Implementations of 'backColor_' :
   CXMenu:backColor_ :     this.backColor_(val)
   SCView:backColor_ :     this.backColor_(color)
また同様にして、「series」に「cmd+Y」で以下が出た。

Implementations of 'series' :
   Meta_SequenceableCollection:series :     this.series(size, start, step)
   SimpleNumber:series :     this.series(second, last)
SuperColliderコードの中に「Meta_SomeClass:」というような表記があれば、それはクラスメソッドである。 またSuperColliderコードの中に「SomeClass:」というような表記があれば、それはインスタンスメソッドである。 「cmd+J」でクラス定義の一覧を見ると、関数の一覧リストを見ることができる。 その中で「*ar」とか「*new-」など、「*」で始まるものは、クラスメソッドである。 アスタリスク「*」の無い名前のものは、インスタンスメソッドである。

SuperColliderの仕様としては。我々が作るクラスは全て、クラスの階層構造の中に組み込まれていて、相互に関係している。 たとえば「SinOsc」というのは、UGenのタイプの一つである。 「Array」というのは、「ArrayedCollection」のタイプの一つである。 このように、タイプが相互に関係していることで、SuperColliderプログラマは(どうせたいていの場合にはサウンド処理をしたいので)、 ちょっと違うだけの関数をいちいちゼロから定義する必要がなく、共通の(お約束の)プロパティを相互に活用できる。

以下は、このようなクラスの「木構造」を確認するために、postウインドウ内で「Collection.dumpClassSubtree」 を実行させた時に出て来た情報である。


Collection.dumpClassSubtree //show all classes derived from Collection

Collection
[
	Interval
	TwoWayIdentityDictionary
		[ ObjectTable ]
	Array2D
	Range
	MultiLevelIdentityDictionary
	[
		LibraryBase
			[ Archive Library ]
	]
	Set
	[
		Dictionary
		[
			IdentityDictionary
			[
				Environment
					[ Event ]
			]
		]
		IdentitySet
			[ OrderedIdentitySet ]
	]
	Bag
		[ IdentityBag ]
	Pair
	SequenceableCollection
	[
		LinkedList
		Order
			[ SparseArray ]
		RingBuffer
		List
			[ SortedList ]
		ArrayedCollection
		[
			RawArray
			[
				DoubleArray
				FloatArray
					[ Wavetable Signal ]
				String
				SymbolArray
				Int32Array
				Int16Array
				Int8Array
			]
			Array
		]
	]
]
これ以上は、「Internal-Snooping」を「cmd+d」で見ろ、とあった。 その中身は「Snooping around SuperCollider」ということで、 このような ものであった。

2011年9月6日(火)

・・・さて、上の部分までは、雨がひどくなってきたために早めにホテルに戻って、 リンツ空港に出発するまでホテルのロビーで書いていたが、ここからはフランクフルトから成田への機内で再開てある。 爆睡から目覚めたボキボキの身体を入念なストレッチで暖め、冷たいオレンジジュースと、熱いブラックコーヒーが染み渡って、 全身の細胞が活動を開始した(^_^)。 いつものようにちょうどフライトの半分過ぎ、 「Time since Departure」(6時間)が「Time to Destination」(5時間)を上回ったあたりである。 既にフランクフルトの現地時間も9月6日の早朝3時となっているので、日付を変えて進めていこう。

クラスとオブジェクトの話の途中だったが、全てはrootとなる基本的なクラスから生まれてくる、という事だった。 混乱を招きかねないが、このrootとなる基本的なクラスをオブジェクトと呼ぶ。 SuperColliderにおいては、root以外の全てのクラスは、オブジェクトクラスのプロバティを継承している。 これを我々は、「全てのクラスは(root)オブジェクトから生まれる」と言う。

SuperColliderの全てのクラスの階層構造は非常に巨大である。 Objectから階層構造の末端に向かってたどって行くと、クラスは次第にどんどん特定されていく。 クラスを詳細に記述していく(末端に向かってたどって行く)のを、「サブクラス」と呼ぶ。 これと逆方向の、あるクラスの上流のクラスを、「スーパークラス」と呼ぶ。 以下の「超・長大なツリー」(^_^;)は、このようなクラスの「木構造」を確認するために、postウインドウ内で「Object.dumpClassSubtree」 を実行させた時に出て来た情報である。


Object.dumpClassSubtree

Object
[
	Interpl
	SwingOSC
	PowerOfTwoAllocator
	LRLPF
	Model
	[
		Server
			[ ScoreStreamPlayer ]
	]
	Env
		[ Penv ]
	BLowPass4
	SCWindow
	[
		SCAbstractModalWindow
			[ SCModalSheet SCModalWindow ]
	]
	InterplChord
	Collapse
	QuarkView
	SimpleController
	Play
	Date
	Exception
	[
		Error
		[
			MethodError
			[
				OutOfContextReturnError
				SubclassResponsibilityError
				MustBeBooleanError
				NotYetImplementedError
				ImmutableError
				ShouldNotImplementError
				PrimitiveFailedError
				DoesNotUnderstandError
					[ BinaryOpFailureError ]
			]
			DeprecatedError
		]
	]
	FormantTable
	KeyCodeResponder
	PatternConductor
	AbstractMDPlugin
		[ TextArchiveMDPlugin ]
	PointSource
	GetFileDialog
	SoundFileViewProgressWindow
	SoundFileFormats
	Vocoder
	TChoose
	Insets
	NamedControl
	EventStreamCleanup
	BHiPass4
	TDuty_old
	HighShelf
		[ RBJHighShelf ]
	UpdateListener
	Instr
		[ InterfaceDef ]
	Notch
		[ RBJNotch ]
	OSCResponderQueue
	AbstractNodeWatcher
	[
		BasicNodeWatcher
		[
			NodeWatcher
			DebugNodeWatcher
				[ AnnotatedDebugNodeWatcher ]
		]
	]
	ControlName
	UnicodeResponder
	FlowVar
	MIDIResponder
	[
		CCResponder
		NoteOnResponder
			[ NoteOffResponder ]
		ProgramChangeResponder
		TouchResponder
			[ BendResponder ]
	]
	JKeyState
	Score
	ZLPF
	SerialPort
	MIDIEndPoint
	SCFont
	GeneralHIDSlot
	SwingDialog
	LRUNumberAllocator
	SoundIn
		[ AudioIn ]
	ServerLogSentEvent
	AbstractPlayControl
	[
		StreamControl
			[ PatternControl ]
		SynthControl
			[ SynthDefControl ]
	]
	StackNumberAllocator
	NodeMapSetting
		[ ProxyNodeMapSetting ]
	BhobLoShelf
	Scale
		[ ScaleAD ]
	VocodeBand
	Harmonics
	PathName
	BusPool
	JSCImage
	GeneralHIDDeviceGUI
	FreeVerb1
	Gradient
	SelectXFocus
	WiiMoteGUI
	JSoundFileViewProgressWindow
	Case
	Condition
	SCMenuRoot
	AutoClassHelper
	Rect
	UGenInstr
	Color
	UniqueID
	Player
	HDR
	HIDInfo
	SCPen
	History
	Message
	Quarks
	SCStethoscope
	JITGui
	[
		NdefGui
		MonitorGui
		ProxyMixer
			[ NdefMixer ]
		TaskProxyGui
			[ PdefGui TdefGui ]
		TaskProxyAllGui
			[ PdefAllGui TdefAllGui ]
		EnvirGui
			[ NdefParamGui ]
	]
	Helper
	TopicHelper
	JPen
	SCIBDrag
		[ SCIBPallatteDrag SCIBMultipleDrag ]
	MIDIIn
	LowShelf
		[ RBJLowShelf ]
	JPeakMeterManager
	HIDDevice
	Volume
	DebugFrame
	ScaleStream
	GeneralHIDInfo
	SkipJack
		[ Watcher ]
	SCIBToolboxWindow
	Speech
	SCIBAreaSelection
	Cocoa
	RingNumberAllocator
	ClientOnTrigResponder
	PMOsc
	Sequencer
	BF
	SCNSObjectAbstract
	[
		CocoaAction
		SCNSObject
			[ NSBundle ]
	]
	ClassBrowser
	ResponderArray
	Quark
	SimpleKDRUnit
		[ KDRMaskTester ]
	MultiTap
	BtoUHJ
	Spkr
	JMouseBase
		[ JMouseX JMouseButton JMouseY ]
	Def
	ScaleInfo
	HistoryGui
	ContiguousBlock
	ModalFreqUGen
	InBus
	SystemSynthDefs
	OSCresponder
	[
		OSCMultiResponder
			[ OSCpathDispatcher ]
		OSCpathResponder
		OSCresponderNode
	]
	AutoCompMethodBrowser
	[
		AutoCompClassBrowser
			[ AutoCompClassSearch ]
	]
	LinkedListNode
	FlowLayout
	SCImageKernel
	OSCBundle
		[ MixedBundle ]
	Inspector
	[
		FrameInspector
		ObjectInspector
			[ MethodInspector FunctionDefInspector StringInspector ClassInspector ]
	]
	EZScroller
	PatchIn
	[
		AudioPatchIn
		[
			ControlPatchIn
			[
				ScalarPatchIn
					[ ObjectPatchIn ]
			]
		]
	]
	SCImage
	GridLayout
	PingPong
	ServerLogReceivedEvent
	BeatSched
		[ OSCSched ]
	JSpeech
	SCIBViewPallatte
	BFEncode
	ProcSink
	WarpOverlap
	Mono
	NetAddr
		[ ServerLog BundleNetAddr ]
	ViewRedirect
	[
		DragSource
		SoundFileView
		DragBoth
		Slider
		CompositeView
		FreqScope
		ListView
		Button
		ScrollView
		TextView
		Knob
		TabletView
		Image
		MultiSliderView
		Slider2D
		NumberBox
		ScopeView
		UserView
		VLayoutView
		View
		HLayoutView
		LevelIndicator
		TextField
		Pen
		Dialog
		Stethoscope
		EnvelopeView
		Window
		DragSink
		PopUpMenu
		FreqScopeView
		StaticText
		RangeSlider
		MovieView
		TabletSlider2D
		Font
	]
	PowerOfTwoBlock
	JSCView
	[
		JSCContainerView
		[
			JSCCompositeView
			JSCTopView
				[ JSCScrollTopView ]
			JSCScrollView
			JSCTabbedPane
			JSCLayoutView
				[ JSCVLayoutView JSCHLayoutView ]
			JSCPlugContainerView
		]
		JSCSoundFileView
		JSCControlView
		[
			JSCListView
			JSCPeakMeter
			JSCScrollBar
			JSCPopUpMenu
			JSCButton
			JSCSliderBase
			[
				JSCRangeSlider
				JSC2DSlider
				JSCSlider
					[ JSCKnob ]
			]
			JSCCheckBox
		]
		JSCMovieView
		JSCScope
			[ JSCFreqScope ]
		JSCStaticTextBase
		[
			JSCTextEditBase
				[ JSCNumberBox JSCTextField ]
			JSCStaticText
			JSCDragView
				[ JSCDragSink JSCDragBoth JSCDragSource ]
		]
		JSCAbstractMultiSliderView
			[ JSCEnvelopeView JSCMultiSliderView ]
		JSCPlugView
		JSCAbstractUserView
			[ JSCTabletView JSCUserView ]
		JSCTextView
	]
	GeneralHID
	Spec
	[
		AudioSpec
			[ MultiTrackAudioSpec EffectSpec AudioEventSpec ]
		NonControlSpec
		[
			EnvSpec
			BusSpec
			BufferProxySpec
			SampleSpec
			HasItemSpec
				[ InstrNameSpec PlayerSpec StreamSpec ArraySpec ]
			ScaleSpec
			EventStreamSpec
		]
		ObjectSpec
		ControlSpec
		[
			ScalarSpec
			TrigSpec
			NoLagControlSpec
			[
				StaticSpec
					[ StaticIntegerSpec ]
			]
		]
		TempoSpec
	]
	PVAna
	HIDDeviceService
	EnvGate
	KeyCodeResponderStack
	ProcProcessor
	Splay
	AbstractSystemAction
	[
		ShutDown
		CmdPeriod
		StartUp
			[ ApplicationStart ]
		AbstractServerAction
			[ ServerBoot ServerQuit ServerTree ]
	]
	SynthDef
		[ ProxySynthDef InstrSynthDef ]
	DocParser
	JStethoscope
	PriorityQueue
	ZPolar
	PageLayout
	SCFreqScopeWindow
	TuningInfo
	BFMonitor
	Position
	Tuning
	HiliteGradient
	Feedback
	DelayWr
	Help
	Crucial
	Vocode
	ControlPrototypes
	Enveloper2
	NumChannels
	XFader
	MLIDbrowser
	JPeakMeterSettings
	CocoaDialog
	VolumeGui
	Tempo
	MIDIEvent
	SlotInspector
	BFMixer
	QuarkDependency
	WiiRemoteGUI
	EZGui
	[
		EZSlider
		EZText
		EZNumber
		EZRanger
		EZKnob
		EZLists
			[ EZListView EZPopUpMenu ]
	]
	CocoaGUI
	EventTypesWithCleanup
	SCImageFilter
	RPlay
	UHJtoB
	Post
	WiiMote
	SpeechChannel
	SplayAz
	Plotter
	NodeProxyEditor
	Mix
	RelativeToParent
	BFDecode
	Module
	JFreqScope
	SplayZ
	QuarkSVNRepository
	Warp
	[
		DbFaderWarp
		ExponentialWarp
		LinearWarp
			[ SineWarp CosineWarp ]
		FaderWarp
		CurveWarp
	]
	MXHIDAbsInfo
	Quant
	NodeMap
		[ ProxyNodeMap ]
	RecordProxyMixer
	Buffer
	BFFreeVerb
	MIDIClient
	ProcMod
		[ ProcModR ]
	AutoDocTestClass2
	JSCWindow
	LocalQuarks
	ZPoint
	IODesc
	PulseDPW
	Speakers
	Monitor
	JFont
	SCViewHolder
	[
		FlowView
		ObjectGui
		[
			TempoGui
			SampleGui
			ServerGui
			MethodGui
			AbstractPlayerGui
			[
				HasSubjectGui
					[ StreamKrDurGui PlayerEffectGui PlayerAmpGui ]
				BeatClockPlayerGui
				KrPlayerGui
					[ ModalFreqGui ]
				SimpleTriggerGui
				PatchGui
					[ InstrSpawnerGui ]
				PlayerPoolGui
				PlayerUnopGui
				AbstractSFPGui
					[ SFPGui ]
				PlayerMixerGui
				PlayerBinopGui
				InterfaceGui
			]
			ServerLogGui
			EnvEditorGui
			KeyCodeResponderGui
				[ KeyCodeResponderStackGui SimpleKDRUnitGui ]
			ServerErrorGui
			EditorGui
			[
				PopUpEditorGui
				NumberEditorGui
					[ KrNumberEditorGui ]
				BooleanEditorGui
			]
			ModelImplementsGuiBody
			StringGui
			CXObjectInspector
				[ ClassGui ]
		]
		SCViewAdapter
		SCButtonAdapter
		[
			ActionButton
				[ ClassNameLabel InspectorLink MethodLabel Tile SelectorLabel ]
			PlayPathButton
				[ PlayButton XPlayPathButton ]
			ToggleButton
		]
		CXMenu
		CXAbstractLabel
			[ ArgNameLabel CXLabel VariableNameLabel ]
		StartRow
	]
	InterPoint
	Midi2FreqUGen
	XIn
	NotificationCenter
	NodeControl
	SCIBPanelWindow
	Unix
	EnvironmentRedirect
	[
		LazyEnvir
			[ ProxySpace ]
	]
	XFader4
	IndexL
	ModalDialog
	TempoBus
	BufferProxy
	[
		ArrayBuffer
		AbstractSample
			[ Sample ]
	]
	SynthDesc
	ProxyMixerOld
		[ NdefMixerOld ]
	SynthDescLib
	SoundFile
	Phrase
	Updater
	PrettyState
		[ PrettyEcho PrettyEat ]
	BFGVerb
	PatchOut
	[
		ControlPatchOut
			[ AudioPatchOut ]
		ScalarPatchOut
			[ UpdatingScalarPatchOut ObjectPatchOut ]
	]
	XInFeedback
	CocoaMenuItem
		[ SCMenuSeparator SCMenuItem SCMenuGroup ]
	JSCMenuNode
	[
		JSCMenuRoot
		JSCMenuItem
			[ JSCMenuCheckItem JSCMenuGroup ]
		JSCMenuSeparator
	]
	BhobHiShelf
	XFaderN
	BhobTone
	InspManager
	HIDDeviceElement
	Sheet
	MXHID
	Node
	[
		Group
			[ RootNode ]
		Synth
	]
	NodeIDAllocator
	ProcEvents
	Reflections
	SelectButtonSet
	BLPF
	Plot
	Document
		[ CocoaDocument ]
	JSCSynth
	GetStringDialog
	WiiNunchukGUI
	NotificationRegistration
	ContiguousBlockAllocator
	MIDIClockOut
	GUI
	Clock
	[
		TempoClock
			[ TempoBusClock ]
		SystemClock
		AppClock
	]
	Bus
		[ SharedBus ]
	BHPF
	JKnob
	Scheduler
	AbstractConsole
		[ SaveConsole SynthConsole ]
	Editor
	[
		NumberEditor
		[
			BooleanEditor
			KrNumberEditor
				[ PopUpEditor ]
			IntegerEditor
			IrNumberEditor
		]
		EnvEditor
	]
	MultiPageLayout
	AbstractConstraint
	[
		SeenBefore
			[ CountLimit ]
		Not
		IsEven
		Every
			[ Any ]
		Constraint
		IsOdd
		Xor
		IsNil
		IsIn
			[ IsNotIn ]
		NotNil
	]
	GeneralHIDSpec
	JavaObject
		[ JTexturePaint JavaObjectD ]
	UI
	Impulsar
	MXHIDSlot
		[ MXHIDLedSlot MXHIDRelSlot MXHIDAbsSlot MXHIDKeySlot ]
	TapN
	ProxyMonitorGui
	OSCService
	Do
	MultiChanRecorder
	SwingOptions
	LPCAna
	CheapVerb
	ZHPF
	Semaphore
	Insp
	OverlapTexture
	Switch1
	UGenHelper
	MethodQuote
	InterplEnv
		[ InterplPairs InterplXYC ]
	SCView
	[
		SCTabletView
		SCUserView
			[ SCConstructionView SCKnob ]
		SCContainerView
		[
			SCLayoutView
				[ SCVLayoutView SCHLayoutView ]
			SCCompositeView
			[
				SCTopView
				[
					SCScrollTopView
						[ SCScrollView ]
				]
			]
		]
		SCMultiSliderView
		SCMovieView
		SCStaticTextBase
		[
			SCDragView
			[
				SCDragSink
					[ SCDragBoth ]
				SCDragSource
			]
			SCNumberBoxOld
				[ SCTextFieldOld ]
			SCStaticText
			SCTextField
				[ SCNumberBox ]
		]
		SCLevelIndicator
		SCScope
			[ SCFreqScope SCSoundFileView ]
		SCTextView
		SCControlView
		[
			SCButton
			SCPopUpMenu
			SCSliderBase
			[
				SC2DSlider
					[ SC2DTabletSlider ]
				SCRangeSlider
				SCSlider
			]
			SCListView
		]
		SCEnvelopeView
			[ SCEnvelopeEdit ]
		SCQuartzComposerView
	]
	InstrAt
	SwingGUI
	GeneralHIDDevice
	ProcSFPlayer
	ClassHelper
	WiiMoteIRObject
	Phaser
	Point
		[ PointArray ]
	JSpeechChannel
	AutoClassHelperTest
	ServerOptions
	GraphBuilder
	NSTypeEncoding
	WiiCalibrationInfo
	HelpSearchResult
	Platform
	[
		UnixPlatform
			[ OSXPlatform ]
	]
	LRHPF
	ScaleLP
	MIDIOut
	Switch
	TestDependant
	SelectX
	Shift90
	SFPlay
	RawPointer
	Magnitude
	[
		Association
		Number
		[
			Polar
			Complex
			SimpleNumber
				[ Float Integer ]
		]
		Char
	]
	Boolean
		[ False True ]
	Nil
	Symbol
	Finalizer
	AbstractFunction
	[
		Pattern
		[
			PbindProxy
			Pkey
			Pseries
			PhidSlot
			PhidKey
			Pbrown
				[ Pgbrown ]
			FilterPattern
			[
				Prorate
				Psync
				Pwrap
				Plambda
				Pseed
				Psetpre
					[ Paddpre Pmulpre ]
				PfadeIn
					[ PfadeOut ]
				Pset
				[
					Padd
					Psetp
						[ Paddp Pmulp ]
					Pmul
				]
				Pgroup
				Pplayer
				Pavaroh
				FuncFilterPattern
					[ Pwhile Pfset Preject Pcollect Pselect ]
				Pstretch
					[ Pstretchp ]
				Pfin
					[ Pfinval ]
				Pstutter
					[ PdurStutter ]
				Pdrop
				Pclutch
				Pn
				PfinQuant
				Pfindur
				Plag
				Psym
				[
					Psym1
					Pnsym
						[ Pnsym1 ]
					Ptsym
				]
				Ptrace
				Prewrite
				Pbus
				Pspawn
				Pfx
					[ Pfxb ]
				Pflow
				Pconst
				Pprotect
				Pbindf
				Pclump
					[ Pflatten ]
				Pdiff
			]
			Pcauchy
			Ppatmod
			Platoo
			Pgbman
			Ptime
			PlinCong
			Prout
				[ Proutine Pspawner ]
			Pindex
			Pbind
			Pfunc
			Ppoisson
			Pgeom
			Pquad
			Pproto
			Phid
			PatternProxy
			[
				Pdefn
				TaskProxy
				[
					Tdef
					EventPatternProxy
					[
						Pdef
							[ Pbindef ]
					]
				]
			]
			Spawner
			Pdict
			Pstep
				[ Pseg ]
			ListPattern
			[
				Pslide
				Pxrand
				Pfpar
				Pwrand
				Pshuf
				Pseq
					[ Pser Place Ppatlace ]
				Ptuple
				Pwalk
				Ppar
				[
					Pgpar
						[ Pgtpar ]
					Ptpar
				]
				Pdfsm
				Pfsm
				Prand
			]
			Plorenz
			Peventmod
			Pget
			Phenon
			Pswitch
				[ Pswitch1 ]
			Pevent
			Pbinop
			Pchain
			Pstep3add
			Pfhn
			Plet
			Pstep2add
			Pmono
				[ PmonoArtic ]
			Pif
			Pwhite
				[ Phprand Pmeanrand Plprand Pexprand ]
			PstepNfunc
				[ PstepNadd ]
			Pbeta
			Pgauss
			Plazy
			[
				PlazyEnvir
					[ PlazyEnvirN ]
			]
			Pstandard
			Pfuncn
			Punop
			Penvir
			Pnaryop
				[ PdegreeToKey ]
			Pprob
		]
		BinaryOpFunction
			[ BinaryOpFunctionProxy ]
		AbstractPlayer
		[
			PlayerUnop
			HasPatchIns
			[
				Patch
				[
					InstrSpawner
					[
						InstrGateSpawner
							[ ScurryableInstrGateSpawner ]
					]
				]
			]
			PlayerMixer
				[ GroupedPlayerMixer ]
			MultiplePlayers
				[ MultiTrackPlayer ]
			AbstractPlayerProxy
			[
				ModalFreq
				Interface
				PlayerSocket
					[ PlayerPool PlayerEffectSocket ]
			]
			KrPlayer
				[ TempoPlayer BeatClockPlayer ]
			AudioInPlayer
			AbstractSFP
			[
				SFP
					[ VSFP ]
			]
			PlayerBinop
			SynthlessPlayer
			[
				Silence
					[ ObjectNotFound PlayerInputProxy ]
				SimpleTrigger
				MIDIPlayer
				[
					CCPlayer
					MIDIHoldsNotes
						[ MIDIFreqPlayer MIDIGatePlayer ]
				]
				AbstractBusDriver
				[
					StreamKrDur
						[ Stream2Trig ]
					BusDriver
				]
			]
			HasSubject
			[
				AbstractPlayerEffect
				AbstractSinglePlayerEffect
					[ PlayerAmp EnvelopedPlayer ]
			]
			MonoAudioIn
		]
		AbstractOpPlug
			[ BinaryOpPlug UnaryOpPlug ]
		BusPlug
		[
			NodeProxy
				[ Ndef ]
		]
		Ref
		[
			RefCopy
			Maybe
				[ Fdef ]
		]
		UnaryOpFunction
			[ UnaryOpFunctionProxy ]
		UGen
		[
			Line
			IEnvGen
			ListTrig
			AbstractOut
			[
				LocalOut
				XOut
				SharedOut
				Out
					[ OffsetOut ReplaceOut ]
			]
			Logistic
			LinRand
			MembraneHexagon
			KeyState
			OSFold8
			LFSaw
				[ LFTri LFCub LFPar ]
			FreeVerb
			PitchShift
			LTI
			ExpRand
			PV_XFade
			FFTTriggered
			RandID
			AtsUGen
			[
				AtsNoiSynth
				AtsNoise
				AtsSynth
				AtsAmp
				AtsBand
				AtsFreq
				AtsPartial
			]
			WeaklyNonlinear
			Compander
			VOsc3
			PSinGrain
			VOSIM
			Pause
			FreeSelf
			StkShakers
			MoogVCF
			NL
			GravityGrid
			SpecPcile
			ScopeOut
			SinOsc
			FFTComplexDev
			SoftClipper8
			ToggleFF
			SinOscFB
			PrintVal
			Convolution2
			Balance
			MaxLocalBufs
			GaussTrig
			MarkovSynth
			Pulse
			FFTPhaseDev
			XFade
				[ XFade2 LinXFade2 ]
			Gendy2
			PulseCount
				[ SetResetFF ]
			Sweep
			InfoUGenBase
			[
				RadiansPerSample
				NumRunningSynths
				SampleDur
				NumBuffers
				ControlDur
				ControlRate
				NumInputBuses
				NumControlBuses
				SubsampleOffset
				NumAudioBuses
				SampleRate
				NumOutputBuses
			]
			SendTrig
				[ SendReply ]
			LastValue
			PV_CommonMag
				[ PV_CommonMul ]
			FreqShift
			CoinGate
			FFTFluxPos
			FFTMutInf
			LPFVS6
			Dbrown2
			CompanderD
			MulAdd
			FFTInterleave
			MoogLadder
			Clipper32
			FitzHughNagumo
			CheckBadValues
			Hasher
			StkBlowHole
			BufInfoUGenBase
			[
				BufChannels
				BufFrames
				BufDur
				BufSamples
				BufSampleRate
				BufRateScale
			]
			Latoocarfian2DN
				[ Latoocarfian2DC Latoocarfian2DL ]
			TExpRand
			Free
			MCLDChaosGen
				[ ChuaL RosslerResL ]
			SwitchDelay
			ZeroCrossing
			LFPulse
			OSWrap8
			PosRatio
			StkMandolin
			StandardTrig
			TBetaRand
			VarSaw
			DynKlank
			HenonTrig
			MouseButton
			Pluck
			LinExp
			GravityGrid2
			PV_MagGate
			Latch
				[ Gate ]
			SawDPW
			Dust2
			MantissaMask
			PlaneTree
			Dust
			ClearBuf
			LorenzTrig
			SoftClipAmp8
			NTube
			CrossoverDistortion
			Onsets
			LPF1
			WeaklyNonlinear2
			Tap
			K2A
				[ T2A ]
			NRand
			JScopeOut
			PeakFollower
			FFTSlope
			SOMAreaWr
			Clipper4
			PVSynth
			DegreeToKey
			SetBuf
			LPCSynth
			PV_Morph
			NeedleRect
			InRect
			AmpComp
				[ AmpCompA ]
			TermanWang
			EnvGen
			TBrownRand
			Coyote
			Select
			Index
				[ Shaper DetectIndex WrapIndex IndexInBetween ]
			AmplitudeMod
			BFDecoder
				[ BFDecode1 FMHDecode1 ]
			FFTSpread
			Maxamp
			Streson
			Instruction
			Logger
			RunningSum
			OscN
			IIRFilter
			LPCAnalyzer
			StkBeeThree
			PV_Compander
			BufWr
			Rand
			FFTRumble
			PauseSelf
			BasicOpUGen
				[ BinaryOpUGen UnaryOpUGen ]
			StkBowed
			Linen
			Clockmus
			PV_MagScale
			COsc
			StkClarinet
			OSTrunc4
			Peak
				[ RunningMin RunningMax ]
			AverageOutput
			JoshGrain
			[
				SinGrain
				InGrain
				FMGrainB
				FMGrainI
				InGrainI
				SinGrainB
				FMGrain
				InGrainB
				SinGrainI
				BufGrain
				MonoGrain
				BufGrainI
				BufGrainB
			]
			Concat
			FFTCrest
			Duty
				[ TDuty ]
			WhiteNoise
				[ PinkNoise BrownNoise ClipNoise GrayNoise ]
			Squiz
			SVF
			SpecFlatness
			TBall
			FFTMKL
			LPF18
			MostChange
				[ LeastChange ]
			SLOnset
			FFTPower
			WaveTerrain
			LFNoise0
			[
				LFClipNoise
				LFDNoise3
				LFDNoise0
				LFNoise2
				LFDNoise1
				LFNoise1
				LFDClipNoise
			]
			LatoocarfianTrig
			DUGen
			[
				Dswitch1
					[ Dswitch ]
				Dseries
				Dpoll
				DbufTag
					[ Dtag ]
				Dbufwr
				Dbrown
					[ Dibrown ]
				ListDUGen
					[ Dshuf Dxrand Drand Dseq Dser ]
				Dgeom
				Dstutter
				Dwhite
					[ Diwhite ]
				Donce
				Dfsm
				Dbufrd
			]
			Breakcore
			Disintegrator
			DelTapWr
			TPV
			Gendy3
			NestedAllpassN
				[ NestedAllpassC NestedAllpassL ]
			Getenv
			FhnTrig
			KmeansToBPSet1
			BufDelayN
				[ BufDelayC BufDelayL ]
			Crackle
			Spring
			PeakEQ4
			StkModalBar
			Metro
			IFFT
			Henon2DN
				[ Henon2DC Henon2DL ]
			Ball
			Osc
			Delay1
				[ Delay2 ]
			Filter
			[
				BPF
					[ BRF ]
				LPF
					[ HPF ]
				InsideOut
				LPZ2
					[ HPZ2 BPZ2 BRZ2 ]
				LagUD
					[ Lag2UD Lag3UD ]
				Decay2
				Decay
				OnePole
					[ OneZero ]
				Resonz
				Friction
				MoogFF
				Median
				Integrator
				DetectSilence
				Lag
					[ Ramp Lag3 Lag2 ]
				LPZ1
					[ HPZ1 ]
				Ringz
				WaveLoss
				FOS
				RLPF
					[ RHPF ]
				MeanTriggered
				MedianTriggered
				MidEQ
				Slew
				LeakDC
				TwoPole
					[ TwoZero APF ]
				BEQSuite
				[
					BBandStop
					BHiPass
					BLowShelf
					BPeakEQ
					BAllPass
					BHiShelf
					BBandPass
					BLowPass
				]
				Slope
				Formlet
				SOS
			]
			KeyTrack
			Clipper8
			OSFold4
			DelTapRd
			SpecCentroid
			GaussClass
			FFTDiffMags
			HilbertFIR
			Loudness
			Standard2DN
				[ Standard2DC Standard2DL ]
			LPCError
			MembraneCircle
			FFTFlatness
			SoftClipAmp
			DoubleWell
			XLine
			Vibrato
			AY
			Formant
			PV_MagMinus
			RMEQSuite
			[
				RMShelf2
				Allpass2
				Allpass1
				RMShelf
				RMEQ
					[ RegaliaMitraEQ ]
			]
			BLBufRd
			OnsetsDS
			StkFlute
			TIRand
			DemandEnvGen
			A2K
				[ T2K ]
			SmoothDecimator
			LinLin
			SoftClipper4
			TRand
			GbmanTrig
			WAmp
			BMoog
			PulseDivider
			FreeSelfWhenDone
			Klang
			RecordBuf
			IRand
			DoubleNestedAllpassN
				[ DoubleNestedAllpassL DoubleNestedAllpassC ]
			Lorenz2DN
				[ Lorenz2DL Lorenz2DC ]
			Fhn2DN
				[ Fhn2DC Fhn2DL ]
			Normalizer
				[ Limiter ]
			DoubleWell3
			Dgauss
			AudioMSG
			Blip
			SoftClipAmp4
			PauseSelfWhenDone
			PartConv
			Perlin3
			TwoTube
			Gendy1
			OutputProxy
			OSTrunc8
			StkPluck
			OSWrap4
			SortBuf
			Done
			Saw
			Phasor
			SkipNeedle
			DynKlang
			CombN
				[ CombL CombC AllpassC AllpassN AllpassL ]
			PV_ChainUGen
			[
				PV_JensenAndersen
				PV_Whiten
				PV_MagMap
				PV_MagFreeze
				PV_MagSubtract
				FFT
				PV_BinBufRd
				PV_RectComb2
				PV_BrickWall
				PV_Freeze
				PV_PhaseShift
				PV_MagAbove
					[ PV_MagClip PV_MagBelow PV_LocalMax ]
				PV_BufRd
				Unpack1FFT
				PV_HainsworthFoote
				FFTTrigger
				PV_BinScramble
				PV_MagMulAdd
				PV_MagLog
				PV_NoiseSynthP
					[ PV_PartialSynthF PV_PartialSynthP PV_NoiseSynthF ]
				PV_MagMul
				[
					PV_Max
					PV_Copy
					PV_CopyPhase
					PV_Mul
					PV_Add
					PV_Div
					PV_Min
				]
				PV_MagDiv
				PV_MagSquared
					[ PV_PhaseShift270 PV_Conj PV_MagNoise PV_PhaseShift90 ]
				PV_PlayBuf
				PV_Diffuser
				PV_OddBin
					[ PV_EvenBin ]
				PV_MagSmear
				PV_BinWipe
				PV_BinPlayBuf
				PackFFT
				PV_MagExp
				PV_MagBuffer
					[ PV_FreqBuffer ]
				PV_SpectralMap
				PV_DiffMags
				Cepstrum
					[ ICepstrum ]
				PV_MaxMagN
					[ PV_MinMagN ]
				PV_RectComb
				PV_RandComb
				PV_RecordBuf
				PV_SpectralEnhance
				PV_ConformalMap
				PV_Invert
				PV_MagSmooth
				PV_RandWipe
				PV_BinShift
					[ PV_MagShift ]
				PV_BinDelay
			]
			Klank
			VOsc
			Convolution2L
			SineShaper
			WalshHadamard
			BufCombN
				[ BufAllpassC BufCombL BufCombC BufAllpassN BufAllpassL ]
			NL2
			Stepper
			LocalBuf
			DriveNoise
			StkVoicForm
			SinTone
			DiskOut
			Convolution3
			InRange
				[ Clip Fold Schmidt Wrap ]
			ListTrig2
			StkBandedWG
			Trig1
				[ Trig TDelay ]
			NLFiltN
				[ NLFiltL NLFiltC ]
			FSinOsc
			Convolution
			Amplitude
			Crest
			Timer
			CombLP
			DelayN
				[ DelayL DelayC ]
			AnalyseEvents2
			Impulse
			FFTPercentile
			DoubleWell2
			Gbman2DN
				[ Gbman2DL Gbman2DC ]
			EnvDetect
			MouseX
				[ MouseY ]
			TWChoose
			PeakEQ2
			Poll
			SyncSaw
			RLPFD
			FrameCompare
			MultiOutUGen
			[
				Panner
				[
					PanX2D
					Rotate2
					PanX
					DecodeB2
					Pan4
					PanAz
					Balance2
					Spreader
					PanB2
					PanB
					BFPanner
					[
						A2B
						BFEncode1
						Rotate
						Tilt
						Tumble
						BFEncodeSter
						B2A
						UHJ2B
						BFManipulate
						FMHEncode2
						B2UHJ
						FMHEncode0
						FMHEncode1
						B2Ster
						BFEncode2
					]
					Pan2
						[ LinPan2 ]
					BiPanB2
					JoshMultiChannelGrain
						[ MonoGrainBF ]
				]
				RosslerL
				ArrayMax
					[ ArrayMin ]
				WarpZ
				GrainIn
				BeatTrack
				AbstractIn
				[
					LocalIn
					SharedIn
					LagIn
					InFeedback
					InTrig
					In
				]
				BufMax
					[ BufMin ]
				CQ_Diff
				Demand
				LPCVals
				AutoTrack
				Warp1
				MdaPiano
				GrainSin
				Pitch
				FFTPeak
				FincoSprottL
				PlayBuf
				TGrains3
				AtsParInfo
				UnpackFFT
				FreeVerb2
				SOMRd
				RMAFoodChainL
				GrainFM
				Control
					[ LagControl TrigControl ]
				AudioControl
				TGrains2
				TGrains
				FFTSubbandFlux
				Hilbert
				Tartini
				PVInfo
				FincoSprottS
				FincoSprottM
				StereoConvolution2L
				DC
				DiskIn
				GrainBuf
				BufRd
				BinData
				VMScan2D
				MFCC
				Goertzel
				GVerb
				SOMTrain
				LoopBuf
				Qitch
				FFTSubbandFlatness
				FFTSubbandPower
				Silent
				BeatTrack2
				SMS
				FFTFlatnessSplitPercentile
				VDiskIn
				JoshMultiOutGrain
				[
					FMGrainIBF
					BufGrainBF
					SinGrainBBF
					BufGrainIBF
					FMGrainBBF
					SinGrainIBF
					BFGrainPanner
						[ InGrainBBF InGrainBF InGrainIBF ]
					SinGrainBF
					FMGrainBF
					BufGrainBBF
				]
			]
			Decimator
			LFBrownNoise0
				[ LFBrownNoise1 LFBrownNoise2 ]
			TrigAvg
			ChaosGen
			[
				FBSineN
					[ FBSineC FBSineL ]
				LatoocarfianN
					[ LatoocarfianC LatoocarfianL ]
				QuadN
					[ QuadL QuadC ]
				HenonN
					[ HenonL HenonC ]
				LinCongN
					[ LinCongL LinCongC ]
				LorenzL
				StandardN
					[ StandardL ]
				GbmanN
					[ GbmanL ]
				CuspN
					[ CuspL ]
			]
			RandSeed
			PV_SoftWipe
				[ PV_Cutoff ]
			TGaussRand
			Gendy4
				[ Gendy5 ]
			StkSaxofony
			TWindex
			WaveletDaub
			LFGauss
			FFTCentroid
			StkMoog
			FFTFlux
			Max
		]
		Thunk
			[ UGenThunk ]
		FunctionList
		NAryOpFunction
		[
			NAryOpFunctionProxy
				[ NAryValueProxy ]
		]
		Function
		Stream
		[
			EmbedOnce
			PmonoStream
				[ PmonoArticStream ]
			UnaryOpStream
			PauseStream
				[ Task EventStreamPlayer ]
			StreamClutch
			FuncStream
			FileReader
				[ CSVFileReader SemiColonFileReader TabFileReader ]
			IOStream
			[
				UnixFILE
				[
					File
						[ ZArchive PVFile LPCFile AtsFile ]
					Pipe
				]
				CollStream
					[ PrettyPrintStream LimitedWriteStream ]
				Pretty
			]
			OneShotStream
			BinaryOpStream
			CleanupStream
			BinaryOpXStream
			NAryOpStream
			Thread
			[
				Routine
					[ FuncStreamAsRoutine ]
			]
		]
	]
	Interpreter
	Process
		[ Main ]
	Frame
	FunctionDef
		[ Method ]
	Collection
	[
		Interval
		TwoWayIdentityDictionary
			[ ObjectTable ]
		Array2D
		Range
		MultiLevelIdentityDictionary
		[
			LibraryBase
				[ Archive Library ]
		]
		Set
		[
			Dictionary
			[
				IdentityDictionary
				[
					Environment
						[ Event ]
				]
			]
			IdentitySet
				[ OrderedIdentitySet ]
		]
		Bag
			[ IdentityBag ]
		Pair
		SequenceableCollection
		[
			LinkedList
			Order
				[ SparseArray ]
			RingBuffer
			List
				[ SortedList ]
			ArrayedCollection
			[
				RawArray
				[
					DoubleArray
					FloatArray
						[ Wavetable Signal ]
					String
					SymbolArray
					Int32Array
					Int16Array
					Int8Array
				]
				Array
			]
		]
	]
	Class
					[.. all metaclasses ..]
]
新しいクラスを作り出す時には、コロン「:」で区切って、元となった既存のスーパークラスを示す。 「 SinOsc : UGen 」 という記述は、 という2つの事を意味している。 これは、「SinOsc」が、サイン波形を生成するためのUGenオブジェクトを、より詳細に特徴付けたクラスであるという事である。 SuperColliderではデフォルトでrootのオブジェクトから始まっているので、元となるオブジェクトから記述する必要はなくて、 オブジェクトのサブクラスを作っていけばよい。

再びキリンの例で言えば、我々のオリジナルのキリンのクラスは、以下のようにコロンで繋いでどんどん詳細に記述する。 階層性を表す木構造は、カテゴリの組み合わせという事もできる。 欲しいクラスの記述は一つに決まるものではないが、判っているカテゴリ化を組み合わせていけば、欲しいクラスの記述に近づいていく。


SavannahBeast: SavannahHerbivore: Giraffe: AlbinoGiraffe
メソッドと同様に、クラスとオブジェクトは記録変数を持つことができる。 これはObject Orientedプログラミングでは、データというのは、そのデータに作用する関数(機能)とともに格納されているからである。 これは常識的なことである。 キリンのクラスにおいて、我々は個々のキリンの具体的な体長とか模様などの特徴(インスタンス変数)も知りたいだろう。 あるいは、キリンというクラスを特徴付ける(他の動物と違う)のはどこか(クラス変数)を知りたいだろう。

クラスの定義において、インスタンス変数とクラス変数は以下のように記述する。


var <>height; //is an instance variable

classvar <>predators; //is a class variable
具体的なコードにおいては、以下のようにインスタンス変数とクラス変数を取り扱う。 インスタンス変数は変数として保存されたインスタンスから呼ばれるのに対して、 クラス変数はクラス名を経由して呼ばれることに注意しよう。

g= Giraffe.new; 

g.height = 2.4; 
	//set the height in metres of this particular Giraffe

Giraffe.predators = [\lion, \hyena, \man]; 
	//set the predators of the entire giraffe species
重要な点はデータのアクセス方法である。 いくつかのデータは、一般のユーザが知る必要のない秘密の計算によって、内部的にのみ使われる。 それ以外のデータは、クラスのユーザなら誰でもアクセスできる。 ここに登場するのが、以下の意味を持つ「<」と「>」の記号である。 クラスの外の誰も、そのクラスのデータにはアクセスできない。 そのクラスを記述したコード自身からだけ、自由にそのクラスのデータにアクセスできる。 オブジェクト指向の登場する以前のコンピュータシステムでは、 あるデータがどこからか書き換えられてしまうというバグが常に心配だったのに対して、 オブジェクト指向のシステムでは、データが書き換えられたのはクラス内部の犯行(^_^;)、ということで探せるわけである。

もし、クラス定義として 「 Giraffe { var < height; } 」 というクラスを作ったとすると、ゲッターがあるので、


g=Giraffe.new; 
h= g.height;
というのは可能である。 ところが、セッターが無いので、

g=Giraffe.new; 
g.height = 2.3;
というのは出来ない、という事である。 ・・・オブジェクト指向の解説としては、ここまでのようである。 果たしてキリンで理解できたのか微妙であるが、今後、SuperColliderソースを見た時に、 また確認していくことになるだろう。

さて、次の短いトピックは「Using extension libraries and recompiling the library」である。 「SuperCollider application directory」の中には、「SCClassLibrary folder」がある。 このディレクトリの中には、拡張子が「.sc」であるクラス定義ファイルを追加したり削除したりできる。 そして、この環境が替わった時には、「cmd+K」でライブラリを再コンパイルして、クラス定義をアップデートできる。

SuperColliderの拡張ライブラリを入手してアップデートした、という事は、たくさんの人々がプログラミングして作った多くのクラスを、 新たに追加して活用できる、という事である(^_^)。 サードパーティのSuperCollider拡張ライブラリをネットでゲットした時に、 「add it into your SCClassLibrary」としつこく言われるのは、 それがコンパイルされることで、SuperColliderの一部として加わるために必要だからである。 (他の場所に置くことも出来る、という話はまた後で出てくるらしい)

そして、次のトピックは「The claret symbol ^」である。 このチャプターの主題であった「オリジナルのクラスを作る」の、いよいよ仕上げである。 最後に注目しておく必要のある記号「^」は、メソッド(関数)の「戻り値」のためのものである。 まず、普通の関数として


f= {
	arg a; 
	a*a
}
	//return the square of the argument  
を定義する。 クラスのためのメソッドとしては、このイコール記号は不要である。 そこで以下のように書く。 これはインスタンスメソッド「f」を定義している、という事である。

f {
	arg a;  
	^a*a
}
ここに突然「^」が出て来たのは不思議だと思うかもしれないが、メソッドはどんな場所でも戻り値を返すために必要だからである。 あるいは以下のように、ロジック中のいろいろな場所で戻り値を返すためである。 普通の関数には、このようなものは無い。

mymethod {
	arg a;  
	if(a==4, ^7);  
	a=(a*9)%7; 
	^if(a==6, 5, a)
}
さて、ようやくここから、「新しいクラスを作る」という実際のサンプルである。 既に NastySynth.sc と、 SuperMario.sc をゲットしていたが、ゆっと登場するようである(^_^;)。 まず、以下のクラス定義を「NastySynth.sc」というファイル名で保存して、 「SuperCollider application directory」の中の「SCClassLibrary folder」に入れる。 そして、「cmd+K」でライブラリを再コンパイルして、このクラスをSuperColliderが使えるようにアップデートしておく。

NastySynth {
	classvar <>wackiness=200;

	*ar { arg wackymult=1; 
		^CombN.ar(
			SinOsc.ar(
				LFNoise0.ar(9, wackymult*wackiness, MouseY.kr(100,400)), 
				7, 
				MouseX.kr(0.0,0.75)
			) % 0.3,
			0.3,
			0.3, 
			5
		)
	}
}

その上で、以下を1行ずつ実行していくと、なかなかファンキーな面白い音がした。サウンドは省略である。(^_^;)


NastySynth.wackiness= 200; 

{NastySynth.ar}.play

{NastySynth.ar(0.01)}.play

{NastySynth.ar(SinOsc.kr(MouseX.kr(0,100)))}.play

NastySynth.wackiness= 4000; 

{NastySynth.ar}.play

{NastySynth.ar(0.01)}.play

{NastySynth.ar(SinOsc.kr(MouseX.kr(0,100)))}.play
そして最後に、いよいよスーパーマリオである。 「SuperMario.sc」を「your DefaultLibrary」に入れてrecompile.せよ、とあるが、これがエラーでできなかった(^_^;)。 コンパイルエラーが起きると、internal windowもlocalhost windowも開かないので、何も出来なくなる。 与えるはずのコードは以下である。(^_^;)

//Now run this client code:

(
	var sm;
	sm=SuperMario(1.5);
	SuperMario.companyaffiliation= \Nintendo;
	sm.graspofItalian= 1.0; 
	sm.dostuff;
)

(
	u=UltraPlumber.new.height_(1.5);
	UltraPlumber.dungareesupplier= \Levi;
	SuperMario.companyaffiliation.postln;
	u.graspofItalian= 0.7; 
	u.dostuff;
)

//and you can run these lines whilst its playing

u.craftiness= 1500;

u.height= 7.3; 

u.ubendification= 0.1;

u.sizeoftool=2.0;
とりあえず、ぼちぼちここらでバッテリが減ってきたので、また後で挑戦することにした。 来週には札幌と神戸の学会ハシゴ1週間出張があるので、そのあたりだろうか。 フライトはロシアの最後あたり、カバロフスク上空、成田まではあと2時間ほどである。 今回はけっこう揺れていて、ジェットコースターも楽しめた(^_^;)。

SuperCollider日記(7)