SuperCollider日記(4)

長嶋 洋一

(一部 Processing日記かも)

Part 1  Part 2  Part 3 Part 5  Part 6  Part 7   


2011年4月12日(火)

新学期が始まってしまった(^_^;)。 前回のこの日記はなんと3月31日。もう2週間の空白である。 Processing の方は、4月5日に書いていたが、それでもそこから1週間の空白である。 ガイダンスが終わり、講義が始まり、3回生の個人面談が連なり、もういきなり全速力あらえっさっさである(^_^;)。

ただしこの期間には、実は目立たないが、もう一つの大きな仕事があったのだ。 これまで2001年からお世話になってきたサーバ業者から、新しいところに引っ越しをしたのである。 物理的には、富山県の山奥にあるらしいデータセンタから、シリコンバレーのデータセンタに、ということらしいが、 インターネット上では何も変わらないように見えるから面白い。 この移転を3月から半月ほど水面下で進めてきたが、ぼちぼちどうやら完了が近づいてきた。 まだ講義初日の昨日とかは、いつものURLでなく「IP直打ち」という暫定運用だが、 学生にすれば滅多にない現象なので、教材としてはこれもOKである。

さて、SuperColliderの続きは、 Workshop materials for Computer Music (G6002)5. Interaction5.3 Further GUI example.html からである。 もう忘却の彼方であるが、たしかGUIをやっていたような気がする。(^_^;)

このセクションには、たった一例のサンプルがあるだけだった。以下である。 こんな音がした。


(
	SynthDef(
		\mysound,
			{
				arg density=100, centrefreq=1000, rq=0.1, amp=0.1;
				var dust, filter; 
				dust= Dust.ar(density);
				filter= BPF.ar(50*Decay2.ar(dust,0.01,0.05),centrefreq, rq);
				Out.ar(0,(filter*amp).dup(2))
			}
	).add
)

(
	var w, slid2d, knob, numberbox; 
	var sound; 
	sound= Synth(\mysound);
	w= Window("mysound's window",Rect(100,300,300,200));
	slid2d= Slider2D(w,Rect(10,10,180,180));
	knob= Knob(w,Rect(210,10,80,80));
	numberbox= NumberBox(w,Rect(210,110,80,80));
	slid2d.action = 
		{ 
			sound.set(\density,slid2d.x*100.0+1,\rq,slid2d.y*0.5+0.01);
		}; 
	knob.action=
		{
			sound.set(\centrefreq,knob.value*2000+500)
		};
	numberbox.action=
		{
			var temp; 
			temp= numberbox.value.max(0.0).min(1.0);
			sound.set(\amp,temp);
			numberbox.value = temp;
		};
	w.front; 
	w.onClose= 
		{
			sound.free;
		};
)

今日はとりあえず、何かやってみた、という事が大きいので、ここまでである。次は Workshop materials for Computer Music (G6002)6. Scheduling からとなる。

2011年5月27日(金)

前回の日記は4月12日。1ヶ月半ぶりである(^_^;)。 そしてこの部分は、セントレアから成田に向かうANAの機内で書き始めた。 成田からミュンヘン、そしてオスロと長いフライトの合間に続きをやろうと、 機内ではネットアクセスできない関連データも事前に仕込んできた。 Processing日記 の方は、音楽情報科学の講義で紹介するのと2人のM1が勉強を開始したので進めていたが、 SuperColliderの方は3月末のワークショップまでは頑張ったが、 目先の目標が無くなるとコレである(^_^;)。

今回は Workshop materials for Computer Music (G6002)6. Scheduling6.1 Scheduling.html からである。 いつものようにlocalhost serverを起動して、最初のサンプル「load me first」は以下である。 これを実行すると、後でスケジューリングに使うSynthDefが定義されるので、それだけでは音は出ない。


(
	SynthDef(\bleep,
		{
			arg out=0, note=60, amp=0.5, pan=0.0;
			var freq, env; 
			freq = note.midicps;
			env = EnvGen.ar(
				Env([0,1,1,0],[0.01, 0.1, 0.2]),
				levelScale:amp, 
				doneAction:2
			);
			Out.ar(
				out,
				Pan2.ar(Blip.ar(freq) * env, pan)
			)
		}
	).add;
)
このSynthDefはenvelopeを持っていて、「doneAction of 2」により発音し、 エンベロープが最後まで行くと自分でちゃんとメモリを解放して終了するという。 この、発音が終わったらメモリを解放するというのは重要で、さもなければメモリが無くなっていってしまう。 ただし、メモリ中に「親」のSynthDefはもちろん残っている。 「doneAction」によって、親のSynthDefから継承され新たに発音する「子インスタンス」が作られて発音し、 その発音が終了すると「子」は自己消去してくれる、という事である。

リピートを実現するには、以下のようにグローバルな「SystemClock」を使って、Synthを呼び出してやればよい。 これを加えて実行するとリピート音が出て、「コマンド+ピリオド」で発音停止する。 Synthの後の数字がsec単位でのインターバルなので、この例では1秒ごとにリピートする。 こんな音がした。 なお、サンブルサウンドの冒頭に何秒かの空白があるのは、localhost serverの「recording」ボタンを押して録音を開始して、 その後、postウインドウでSuperColliderソースの全体をハイライトさせてenterするまでの時間なので、 この部分は軽く無視して欲しい。(^_^;)


(
	SystemClock.sched(
		0.0,	//start at 0.0 sec from now, i.e. immediately
		{	//a function which states what you wish to schedule
			Synth(\bleep);
			1	//repeat every second
		}
	)
)

・・・ここまででもう成田に着いてしまった。沖縄便ならともかく、中部→成田では機内の時間は限られる(^_^;)。 この部分は成田空港のラウンジでMacBookAirを充電しながらビール片手に書いているが、 エコノミーで座席コンセントの無い機材なので、ミュンヘンまでのフライトではあまり進めない気がする。 SuperColliderはけっこう、バッテリーを喰う模様である。

同じSynthDefが起動(常駐)しているところで、以下の例では、ランダム間隔であるスケールからランダムに選んでランダムな定位で発音している。 こんな簡単なものでアルゴリズミック作曲が出来る、というわけである。 なお、「#[0,2,4,5,7,9]」というのは、「fixed (non-dynamic) Array」であり、普通のdynamicな配列(後でも中身を改変可能)に比べてメモリを喰わないのだという。 この程度をケチってもあまり変わらない気もするが。 こんな音がした。


(
	SystemClock.sched(
		1.0,		//start in 1.0 sec
		{
			Synth(\bleep, 
				[	//passing in arguments to the Synth
					\note, (#[0,2,4,5,7,9] + 48).choose, 
					\pan, 1.0.rand2
				]
			);
				// random choice of repeat time:
			[0.25,0.3,0.7,0.1].choose
		}
	)
)

絶対的な「現在のシステムクロック時間」を得るためには、「Main.elapsedTime;」を使える。 以下の例では、起動してから1秒後に発音がスタートする。聞いた感じは変わらないが(^_^;)、 こんな音がした。


(
	SystemClock.schedAbs(
		Main.elapsedTime+1.0,
			//start at absolute system clock time now + 1 second
		{
			Synth(\bleep, 
				[	//passing in arguments to the Synth
					\note, (#[0,2,4,5,7,9] + 48).choose, 
					\pan, 1.0.rand2
				]
			);
				// random choice of repeat time:
			[0.25,0.3,0.7,0.1].choose
		}
	)
)

音楽のタイミングをSuperColliderでプログラミングする上では、小節あたりのビート数のBPMだけでなく、 以下のような毎秒のビート数であるBPSの知識も重要である。


1 bps = 60 bpm

1.6666667 bps = 100 bpm

2 bps = 120 bpm

2.4 bps = 144 bpm

3 bps = 180 bpm
ビート(拍)単位のスケジューリングにおいては「TempoClock:」を使う。 以下の例では、120bpm、すなわち毎分120発、つまり0.5秒ごとの繰り返しとなる。 システムクロックに代わって「TempoClock」を使うと、この値は「テンポチェンジ」で変わってくれるメリットがある。 こんな音がした。

(
	var t;
	t = TempoClock(2); // make a new tempoclock at tempo 120 bpm = 2 beats per second
	t.schedAbs(0,
		{ arg ... args;		// start at absolute beat 0 immediately
			args.postln;	//  post the input arguments to our event function 
				//  (will post logical time in beats, elapsed time 
				//  in seconds of enclosing thread and this clock)
			Synth(\bleep);	// make a bleep
			1.0	// reschedules every beat
		}
	)
)

「TempoClock」は以下のようにdefaultで定義されている。


t= TempoClock.default;
これに対して、以下のようなパラメータを使うことが出来る。

t.elapsedBeats;	//what exact logical beat time are we at

t.bar;	//which bar are we in (default assumption is 4/4)

t.elapsedBeats.ceil;	//find next beat

t.elapsedBeats.floor;	//find last beat
以下の例では、next beatを取得してフレーズを生成している。 「[1,2,3,4].wchoose([0.8,0.1,0.07,0.03]);」というのは、配列をランダム選択する重み付けを指定するためのものである。 こんな音がした。 ・・・成田空港ラウンジでの1時間ちょっとでは、このあたりまでである。

(
	var t;
	t = TempoClock.default;	// the default TempoClock might have been running 
		// for a very long time already, so you should start at the next beat.
	t.schedAbs(
		t.elapsedBeats.ceil, // start at next whole beat
		{
			Synth(\bleep, [\note, [36,40,43].choose, \pan, 1.0.rand2]);
			[0.25,0.5,1.0, nil].wchoose([0.5,0.4,0.05,0.05]);
				//weighted choice
				// repeat at some number of beats from the array- nil means stop
		}
	)
)

・・・同じ日付であるが、この部分からはロシア上空での再開である。12時間以上のフライトも残り5時間半ほどである。 成田を出て昼食の時にクウォーターのワインを3本飲んで爆睡4時間。 ここからあとは現地に着くまで眠らない、というのがいつもの時差ぼけ対策である。 今朝は朝5時に起きてスタートしたが、パソコンの日本時間は既に18時過ぎなのに、機内のミュンヘン時刻はまだ午前11時。 乗り継ぎのフライトはミュンヘン発19時過ぎで、オスロには21時過ぎに着くので、この5月27日というのは途方もなく長い。 そのぶん、帰途では1日が消えることになるが。

既に起きているビートを問い合わせた場合には、SuperColliderのスケジューラはイベントキューから速やかにビートを返す。 以下の例では、毎秒1.2ビートの繰り返しをテンポとして設定し、 -5秒(過去に5秒遡る)の時点から半分の大きさでビートを刻み、 「現在」に来たところで大きく鳴って、5秒が経過したところでストッブする。 こんな音がした。


(
	var u;
	u = TempoClock(1.2);		// make our own Tempoclock at a tempo of 1.2 bps
		// should have started 5 beats ago!
	u.schedAbs(-5.0,
		{
			Synth(\bleep);  0.5 
		}
	);
	// you'll get a loud burst of events as it catches up
	SystemClock.sched(5.0, 
		{
			u.clear
		}
	);
	// schedule a stop for 5 seconds from now.
)

刻々と途中でテンポを変更しても、スケジューラはちゃんとスムースに補間して動作する。 以下の例では、刻々と途中でテンポを変更している。 こんな音がした。


(
	var u;
	u = TempoClock(3.5);
	u.schedAbs(0.0, 
		{
			arg beat, sec; 
			[beat,sec].postln; 
			Synth(\bleep, [\note, rrand(60.0,67.0)]);   
			0.5
		}
	);
	u.schedAbs(8.0, { u.tempo_(2); nil }); // just schedule tempo change
	u.schedAbs(12.0, { u.tempo_(7); nil }); // just schedule tempo change
	u.schedAbs(17.2, { u.tempo_(1); nil }); // just schedule tempo change
	SystemClock.sched(7.0, { u.clear; }); // schedule a stop for 7 seconds from now.
)

テンポの変更の方法は以下のようにいろいろある。


t=TempoClock(2);
t.tempo; //gets current tempo
2

t.tempo_(4); //sets current tempo (only the underscore character is different)
t.tempo;
4

t.tempo= 2.3; //also assigns new tempo, same as last line
t.tempo;
2.3
せっかくGUIに対応したSuperColliderなので、テンポをGUIでぐりぐり変えたくなる。 Max5であればスライダでmetroの数値を変えるだけであるが(^_^;)、以下の例のようにSuperColliderでも出来る。 こんな音がした。

// slider range is always 0.0-1.0, so mapped in the code
(
	var w,u,slid, button;
	w = Window("tempo control test", Rect(100,100,200,40));
	slid = Slider(w, Rect(0,0,200,20));
	button = Button(w, Rect(60,20,40,20)); 
	button.states_([["kill"]]);
	w.front;
	slid.action_({u.tempo_(2*(slid.value)+1)});
	button.action_({u.clear; w.close;});
	u = TempoClock(1);
	u.schedAbs(0.0, 
		{
			arg beat,sec; 
			[beat,sec].postln; 
			Synth(\bleep, [\note, rrand(60, 100)]); 
			1.0
		}
	);
)

SuperColliderでは以下のように、複数のクロックを起動することで、複数のテンポが混在するスケジューリングも容易である。 以下の例では「12:13」というテンポを同時に走らせて、10.9秒後に停止させている。 こんな音がした。


(
	var u,v;
	u = TempoClock(1.2);
	v = TempoClock(1.3);
	u.schedAbs(0, { Synth(\bleep, [\note, rrand(41.8,47.5), \pan, -0.5]);  1.0 });
	v.schedAbs(0, { Synth(\bleep,[\pan, 0.5]);  1.0 });
	SystemClock.sched(10.9, { u.clear; v.clear; }); 
		// schedule a stop for 10.9 seconds from now.
)

SuperColliderのUIはSystemClockからの呼び出しでは更新されない。 そのため、OSにあるAppClockを使うことになる。 以下の例がその簡単なデモサンプルである。 この例では、ウインドウを閉じる前に「cmd+ピリオド」で停止させることが重要である、とあったが、 「cmd+ピリオド」で停止させずにこのウインドウを閉じれるとすれば凄い達人である(^_^;)。


(
	var w, i;
	i = 0;
	w = Window("My Window", Rect(100, 0, 200, 50)); 
		// A 200 by 200 window appears at screen co-ordinates (100, 0)
	w.front;
		//schedule moves and resizes for the window
	AppClock.sched(0.0, 
		{ 
			w.bounds_(Rect(100, (10 * i) % 500, rrand(200,400), 50)); 
			i=i+1; 
			0.125
		}
	);
)

ここで、有用なショートカットのトリックがあるという。 SystemClockで動いているプログラムを以下のように設定する。 これにより、バックグラウンドでAppClockにアサインされるという。


{
	//GUI code
}.defer 
以下は、このテクニックによって、本来はAppClockでないと更新されないウインドウ(UI)を、 他の処理と共通のSystemClockで駆動した例である。

(
	var w, i;
	i = 0;
	w = Window("My Window", Rect(100, 0, 200, 200)); 
		// A 200 by 200 window appears at screen co-ordinates (100, 500)
	w.front;
	SystemClock.sched(0.0, 
		{
			{ w.bounds_(Rect(100, (10*i)%500, 200, 200)) }.defer; 
			// defer acts as a bridge between SystemClock and AppClock
			i=i+1; 
			0.125
		}
	);
)

・・・これで、なんとか機内で 6.1 Scheduling.html が終わった。 僕は海外出張中、腕時計はそれぞれの現地時間に変えるものの、パソコンの時計は日本時間のままにしている。 いま日本は19:25。 フライトは11:50の成田出発からずっと昼間のままで太陽を追いかけて、ミュンヘンは12:25、まだまだロシア上空である。

上空では、というか機内が涼しいためか、バッテリの減りがそれほどでもない。 おそらく、機内を暗くしているのでMacBookAirの液晶とキーボードのバックライトを落としているのが大きいのだろう。 そこで、もう少し進めてみることにした。 次は Workshop materials for Computer Music (G6002)6. Scheduling6.2 Routines and Tasks.html である。 ここでもlocalhost serverを起動させ、上の例で使ったSynthDefをロードしておく必要があるという。 そのまま続けて進めるのは好都合である(^_^)。

スケジューリングのための関数においてはきちんと明示的にスケジュール管理するが、 一般的にプログラムの中の個々の部分の実行順序はブラックボックスである。 Max/MSPであれば「画面内で右から左に処理」などのルールがあるが、 クリチカルに規定するにはtriggerオブジェクトを使う。 これのSuperCollider版、というのがここでのトピックである。

ルーチンの実行順序を取得するのが、以下の「yield」という属性である。


(
	r=Routine(
		{
			1.yield; 
			2.yield;
			3.yield;
		}
	)
)
a Routine

r.value; //run this line four times
1

r.value; //run this line four times
2

r.value; //run this line four times
3

r.value; //run this line four times
nil
以下が、別のサンプルである。

(
	var r;
	r = Routine(
		{
			var x;
			x = 1.0.rand;
			2.yield;
			x.yield;
			1000.yield;
			x.yield;
			x = 1.0.rand;
			x.yield;
		}
	);
	10.do({ r.value.postln });
)

以下の例では、「r.value」あるいは「r.next」はいずれも"yield value"を返す。 実行してみると、2回目と4回目にサウンドも鳴っている。


(
	r = Routine(
		{
			1.yield;
			Synth(\bleep);
			2.yield;
			1.yield;
			Synth(\bleep);
			1.yield;
		}
	);
)

r.next; // btw. r.next is a synonym. r.value or r.next both return the "yield value".
1

r.next;
2

r.next;
1

r.next;
1
このRoutineを使っても、以下のように簡単な自動演奏が実現できることになる。 こんな音がした。

(
	r = Routine(
		{
			0.5.yield;
			Synth(\bleep);
			1.yield;
			0.5.yield;
			Synth(\bleep, [\note, 43]);
			0.5.yield;
		}
	);
	SystemClock.sched(0, r);
)

しかし、もっともよく使われるのは「play」を使っての呼び出しである。 以下の例では、上の2行を実行すると上記と同じサウンドが鳴り、下の2行を実行するとテンポアップしてサウンドが鳴る。


r.reset; // reset transforms the routine back into its original state
r.play(SystemClock); 

r.reset;
r.play(TempoClock(3));
「yield」はどんなオブジェクトからもリターンできる。 しかし、時間を管理する変数には、浮動小数点値か整数値が必要である。 そのため、相対時間を扱うためには、以下のように「yield」の代わりに「wait」を使う。

TempoClock.default.tempo_(1); 
		// just making sure default is sensible. In actual fact, 
		// for a tempoclock going at 1 bps, time in beats is 
		// the same as time in seconds 

(
	var r;
	r = Routine.new(
		{
			"I just began!".postln;
			1.0.wait;
			"1 second later".postln;
			2.0.wait;
			"finished".postln;
		}
	);
	r.play; //defaults to TempoClock.default;
)

以下のシンプルな例は、ここまでのまとめである。 こんな音がした。


(
	var r;
	r = Routine.new(
		{
			16.do(
				{
					arg i; 
					Synth(\bleep, [ \note, 36+(3*i) ]);
					0.25.yield;
					//  yield and wait mean the same thing 
				}
			);
		}
	);
	r.play;
)

「inf.do」を使うと、以下のように無限ループを簡単に実現できる。 ただし、「.wait」コマンドに何らかの正の値を設定するのを忘れてはいけないので注意が必要である。 この有限ステップ値の設定を忘れると、時間ゼロで無限に繰り返すことで、SuperColliderが、 あるいはコンピュータ自体がクラッシュする(^_^;)。 こんな音がした。


(
	var r;
	r = Routine.new(
		{
			inf.do(
				{
					arg i; 
					Synth(\bleep, [ \note, 36+(3*(i%10)) ]);
					//added %10 to stop it going up forever
					0.25.wait;   //do not miss me out!
				}
			);
		}
	);
	r.play;
)

「Routine」の代わりに、以下の「Task」を使うと、ポーズとリスタートが出来る。 バッテリが50%を割ったので、サンブルサウンドは省略である(^_^;)。


// a Task is a Routine that can be paused and resumed:

(
	t = Task.new(
		{
			inf.do(
				{
					arg i; // keep going forever (until stopped externally)
					Synth(\bleep, [\note, 36+(2.3*(i%17))]);
					0.25.wait; 
				}
			);
		}
	);    
)

t.play(TempoClock(1.4)); //start the Task going

t.pause;  //pause

t.resume;  //unpause
そろそろこのセンションも終わりに近づいてきた。 以下のショートカットも便利だという。

{}.fork
この「fork」を使うと、好きな関数をラップしてplayする。その際にテンポクロックが使えるわけである。 以下の例では1秒ごとに「hello」を表示した。

{5.do{"hello".postln; 1.0.wait} }.fork(TempoClock(1))
a Routine
hello
hello
hello
hello
hello
そして以下がここでの最後のサンプルである。 ちょうど、「140文字のSuperCollider」のような世界である。 こんな音がした。

(
{16.do{arg i; Synth(\bleep, [\note,rrand(48,84) ,\amp, rrand(0.0,0.125)]); 0.125.wait} }.fork(TempoClock(2))
)

これでこのセクションも終わりである。 日本時間は20:35で、飛行機はモスクワ付近となった。 ミュンヘンまではあと2時間50分ほど。ぼちぼち夕食かな。 次はいつになるか、 Workshop materials for Computer Music (G6002)6. Scheduling6.3 Buses.html である。

2011年5月30日(月)

前回の日記を書いた金曜日にオスロのホテルに着いたのは22時半ごろだったが、北欧圏でさらにサマータイムのために、 ようやく日没という時刻で、まだ明るかった。 ただし日の出は午前4時過ぎである。「夜」が6時間しかない(^_^;)。 翌土曜日と日曜日はNIME2011のWorkshop/Tutorialの期間であるが、 あまりソソラレるものが無かったので事前登録せず、時差ぼけ解消と土地勘の育成のためにぼちぼち歩き回った。 そして日曜日の15時過ぎにオスロ音楽アカデミーのロビーに集合して、 オープニングコンサート/レセプションに向かうバスに乗り込むところから、2011年のNIMEがスタートした。

そして月曜日、5/30である。 晩にはコンサートセッションでのパフォーマンスもあり、僕にとってはいきなり今年のNIMEの最大の日である。 この部分は、初日冒頭のオープニングに続く、Keynote Speechの内職として書いている(^_^;)。 全ての会場でNIME参加者には無線LANのパスワードが提供され、メイルの確認を含めて快適である。 Keynote Lecture 1はTellef Kvifte (University of Oslo)であり、 タイトルは「Musical Instrument User Interfaces: the Digital Background of the Analog Revolution」である。 冒頭いきなり民族音楽に合わせてフリーJazzをサックスで演奏したこの人は、元プロデューサーからオスロ大学の先生になったらしい。 リード楽器にfocusしたアナログ的なこだわりのお話で、音楽的発想の基礎としてスケールの体系がある。 また、ミキシングエンジニア出身ということで、 アナログシンセのパネル(→ソフトシンセや音響信号処理ソフトのGUI)などにもこだわりがあった。 このSuperCollider日記は別にNIMEレポートではないので(^_^;)、ここからはNIMEの詳細は省略する。

さて内職のSuperColliderであるが、続きなので Workshop materials for Computer Music (G6002)6. Scheduling6.3 Buses.html からである。 ここでもlocalhost serverを起動し、まずはUGenから始めるという。

SuperColliderサーバは、defaultでサウンド入力とサウンド出力に割り当てられるものとして、 最大で128チャンネルのバスを持っている。 「ServerOptions class」によって、ユーザが入出力のチャンネルを以下のように指定できる。 ただし、この指定は現在すでに起動しているSuperColliderサーバの設定には影響せず、 いったんquitしたサーバを次に起動する際にこれが有効となる。 従って、新しい設定というのは、「事前に設定する」という事が重要である。


//default 
Server.local.options.numOutputBusChannels = 8;
Server.local.options.numInputBusChannels = 8;
//you might change this to 2 in 2 out for a straight stereo setup
例えば、よくあるパターンとして「8入力・8出力」と設定した場合、SuperColliderは以下のように割り当てる。

0-7 are the 8 outs
8-15 are the 8 ins
16-127 are for whatever rendering purposes I desire. 
SuperColliderで指定したとしても、実際にどのようなサウンド動作が実現されるかは、 パソコンのハードウェア(サウンドボード等)に依存する。 以下の例では、実行させるとMacBookAirではイアホンの左だけから音がした。 内職中なのでイアホンである(^_^;)。 こんな音がした。

{Out.ar(0,SinOsc.ar(440,0,0.1))}.play

以下の例では、実行させるとMacBookAirではイアホンの右だけから音がした。 こんな音がした。


{Out.ar(1,SinOsc.ar(440,0,0.1))}.play

以下の例では、実行させるとMacBookAirでは何も音かしなかった。 ゼロが左、1が右で、2はもうステレオではサポートされていないから当然である。


{Out.ar(2,SinOsc.ar(440,0,0.1))}.play

以下の例では、実行させるとオーディオ入力がMacBookAirではイアホンの左にスルーされる。 こんな音がした。 聞こえているのは、Keynote Speechの質疑応答の模様である(^_^;)。


{Out.ar(0,In.ar(8,1))}.play

ここまで書いたところで無事にKeynoteが終わり、最初のpaper sessionをパスしてリハーサル会場のChateau Neufに行き、 無事にセッティングとリハーサルを終えた。 本番に備えてテンションを維持したいので、通しリハを1回だけである。 翌日だと思っていたらNIME steering committee meeting(国際組織委員会)がこの日のランチブレークだったので、 今年は会場に来ていながら参加できなかった(^_^;)。

・・・で、ここはオスロ大学図書館の2階、NIME発表会場の会議場(1階)のすぐ上、poster/demoセッション会場(3階)のすぐ下である。 busの項目は少ないようなので、サッサと片付けてしまおう。 正確な定義として、nチャンネルのサウンドを規定して、それをチャンネルxに割り当てた場合、nチャンネルのバスはxからx+n-1になる。 まぁこれは当然である。 以下の例ではバスを0チャンネルに割り当てているので、最初のチャンネルが0に、次のチャンネルが1になり、結果ととしてステレオになる。


{Out.ar(0,SinOsc.ar([440,880],0,0.1))}.play
以下の例ではサウンドの入力と出力にそれぞれ8チャンネルを規定し、8つの入力がそれぞれ8つの出力に対応してスルー出力される。

{Out.ar(0,In.ar(8,8))}.play
以下の例では、16チャンネルのサウンド出力バスが、バス16-31に割り当てられている。ただし聞こえない。(^_^;)

{Out.ar(16,SinOsc.ar(Array.series(16,400,100),0,0.1))}.play
以下の例では、16チャンネルのミキサー出力をセンター定位にミックスしてモニタしている。 ただし「InFeedback」については後で解説するので、実行時の呼び出し順序などについての解説も無い。

{Out.ar(0,Pan2.ar(Mix.ar(InFeedback.ar(16,16)),0.0))}.play
これで 6.3 Buses.html は終わりである。 次は Workshop materials for Computer Music (G6002)6. Scheduling6.4 subscheduling.html からとなる。 ぼちぼちposter/demoセッションなので、それを覗いてからpaper会場に戻って、さらに続けてみよう。

・・・ということで、たった1行の空白の間に1時間ほど経過した。 駆け足でposter/demoセッションを覗いたが、予想通りに、キネクトを画像センサとしたシステムの発表が多かったが、 どうしてもキネクトである必然性もメリットも見出せなかった。 また、NIME steering committee meetingはやはり明日である事が判明した(^_^)。 そしてProceedingsも駆け足で最初のpaper sessionからposter/demoセッションまでを眺めて、 ようやく落ち着いてpaper発表を聞きながらの内職である(^_^;)。

前に登場したのと同じ「\bleep」という名前のSynthDefをまず起動しておいて、以下の例は、 「他のプロセスの中で起動されるプロセス」の例である。 こんな音がした。


(
	{
		4.do
		{
			arg j; 
				{
					8.do
					{
						arg i;
						Synth(\bleep, 
							[
								\note,48+(i*3.3)+j,
								\amp,
								(1.0-(i/8))
							]
						);
						0.5.wait;
					}
				}.fork;
			4.0.wait;
		}
	}.fork;
)

次の例では、新たに簡単なSynthDefを2つ、以下のように定義して起動(add)する。 テストとして鳴らすのは「Synth(\sound1)」と「Synth(\sound2)」である。


(
	SynthDef(\sound1,
		{
			arg freq=440,amp=0.1; 
			var sound; 
			sound= LPF.ar(Saw.ar(freq),2000)*Line.kr(1,0,0.1, doneAction:2)*amp;
			Out.ar(0,Pan2.ar(sound,0.0))
		}
	).add;
	SynthDef(\sound2,
		{
			arg freq=440,amp=0.1; 
			var sound; 
			sound= HPF.ar(LFPar.ar(freq),1000)*Line.kr(0,1,0.5, doneAction:2)*amp;
			Out.ar(0,Pan2.ar(sound,0.0))
		}
	).add;
)

この準備の上で、スケジューリングのネスティングのデモが以下である。 異なる長さのサウンドを異なる長さの小節で鳴らしている。 こんな音がした。


( 
	var barlengths= [4.0,3.5,5.0];  
	var t= TempoClock(2.5);
	{ 
		inf.do
			{
				|i|  
				var barnow= barlengths.wrapAt(i); 
				"new bar".postln;
					{ 
						var whichsound; 
						whichsound= [\sound1,\sound2].choose;
						((barnow/0.25)-2).floor.do
							{
								|j| 
								Synth(whichsound,
									[
										\freq,
										300+(100*j),
										\amp,0.3
									]
								);  
								0.25.wait; 
							};  
					}.fork(t); 
				barnow.wait; 
			} 
	}.fork(t) 
)

他のサンプルは以下である。 fork(プロセスの起動)のネスティングにより、複雑な音楽を簡単な記述で生成できる。 こんな音がした。


( 
	var t= TempoClock(2); 
	{
		4.do
		{ 
			"bar".postln; 
			{ 
				[1.0,1.0,0.5,0.5,0.5,0.25,0.25].do
					{
						|val|
						Synth(
							\bleep,
								[
									\note,rrand(48,84),
									\amp,
									rrand(0.0,0.3)
								]
						);
						"event".postln;
						val.wait;
					};  
			}.fork(t); 
			4.0.wait; 
		} 
	}.fork(t) 
)

次のサンブルデモが、このセクションの最後である。 いろいろなパートが異なる順序で生成されて音楽的な構造を実現する、という例である。 この例をより大きなセクションの繰り返しに発展させようとした場合には、 カプセル化に伴う繰り返しを避けるために、 各パートを関数の形に記述するように注意しなければならない。 こんな音がした。


( 
	{ 
		SynthDef(
			\bleep,
				{
					arg out=0,
					note=60,
					amp=0.5,
					pan=0.0; 
					var freq, env;  
					freq = note.midicps; 
					env = EnvGen.ar( 
						Env([0,1,1,0],[0.01, 0.1, 0.2]), 
						levelScale:amp,  
						doneAction:2 
					); 
					Out.ar(
						out, 
						Pan2.ar(Blip.ar(freq) * env, pan) 
					) 
				}
		).add; 
		SynthDef(
			\bleep2,
				{
					arg out=0,
					note=60,
					amp=0.5,
					pan=0.0; 
					var freq, env;
					freq = note.midicps; 
					env = EnvGen.ar( 
						Env([0,1,1,0],[0.1, 0.1, 0.02]), 
						levelScale:amp,  
						doneAction:2 
					); 
					Out.ar(
						out, 
						Pan2.ar(Blip.ar(freq, Line.kr(10,1,1)) * env, pan) 
					) 
				}
		).add; 
		SynthDef(
			\mlpitch,
				{
					var soundin, amp, freq, hasFreq;  
					soundin= SoundIn.ar;  
					amp= Amplitude.kr(soundin); 
					#freq, hasFreq= Pitch.kr(soundin);  
					Out.ar(0,amp*SinOsc.ar(freq)) 
				}
		).add;  
		s.sync;  
			//make buffers; 
			//s.sync; 
			//three sections 
//1. 
		10.do
			{
				|i| 
				Synth([\bleep, \bleep2].choose); 
				0.15.wait; 
			}; 
		1.0.wait; 
//2. 
		a= Synth(\mlpitch); 
		1.0.wait;  
		a.free;  
		1.0.wait;  
//3.
// two forks happen simultaneously (note no gap between the two)
	{ 
		100.do
			{
				|i| 
				Synth(
					[\bleep, \bleep2].choose,
					[\note, ([60,62,64,66,67,69,71]-12).choose]
				); 
				rrand(0.05,0.15).wait; 
			}; 
	}.fork;  
	100.do
		{
			|i| 
			Synth(
				[\bleep, \bleep2].choose,
				[\note, [60,62,64,66,67,69,71].choose]
			); 
			0.1.wait; 
		}; 
	}.fork;  
) 

これで 6.4 subscheduling.html は終わりである。 ちょうどpaper session Dの3件が終わったところである。 さすがに内職していると中身はあまり入ってこなかった(^_^;)。 次は Workshop materials for Computer Music (G6002)7. Sound Synthesis 3 からとなる。 サウンドバッファの取り扱いとか、グラニュラシンセシスなど、見出しだけでソソラレルところだが、 次のセッションの後で全員でcity hallに移動してのレセプション(Oslo市長の招待)、 そして再び全員で戻ってきてのコンサートセッションでは、 パフォーマンスが待っている。 ぼちぼち集中するために、今日はここまでである。

2011年5月31日(火)

NIME2011レポートではないので詳細は省略するが、無事に5/30のスケジュールは全て満喫できた。 朝9時にオープニングが始まって、晩のクラブコンサートが終わったのは深夜の23時、 というのはなかなかハードだが、無事に好評のうちに公演も終わって、その後のビールも美味しかった(^_^)。

さて、この日は朝から冷たい雨の降る中、9時からpaper sessionが始まった。 iPhoneとiPadのオンパレードであるが、まぁこれがブームなのかな。 こちらは粛々と内職にて(^_^;)、 Workshop materials for Computer Music (G6002)7. Sound Synthesis 37.1 Buffers and Sound Files.html から進めることにする。 ここでは以下のように、サーバを「s」として特定するために、ボタンでなくpost window内からlocalhost serverを起動しておく。


Server.default=s=Server.local;
s.boot;

ディスクのサウンドファイルでなくストリーミングのサウンドに対して、 サンブル再生したり再生制御(スタート/ストップ/ポーズ)したり信号処理するためには、 SuperColliderサーバではメモリ上のバッファを処理する事が必要になる。 SuperColliderではdefaultで1024個のバッファメモリが用意されていて、OSに対応してメモリ上に配置される。 これを取り扱うには、「Buffer」というwrapper classが便利である。

以下の例では、1チャンネルで441000サンプルフレームのメモリをallocate(配置の予約)する。 SuperColliderの標準サンプリング周波数は44.1KHzなので、これは「10秒分のサンプル」に相当する。


b=Buffer.alloc(s, 10*44100, 1);
	// s= server, 10*44100 num frames, 1 = 1 channel, i.e. mono
メモリの使用状況の確認とメモリの解放は以下のように行う。 MacOSXに標準の「アクティビティモニタ」でもメモリの使用状況はモニタメモリできる。

b.bufnum
	//which buffer are we using?

b.free
	//restore that memory and free that bufferID
「1秒」というような些細な量ではよく判らないので(^_^;)、ちょっとイジワル実験してみた。 まず、以下はSuperColliderを起動しただけのメモリの状況である。 このMacBookAirはメモリを2GBしか積んでいない。

ここで、以下のように「2*60*60*44100」サンプル、つまり「2時間のサウンド」を定義してみた。 実は「24時間」とやったらエラーで断られたのである。 freeエリアが2GB中8MBと、もメモリは限界ぎりぎりまで使われている。


b=Buffer.alloc(s, 2*60*60*44100, 1);

ここで、以下のようにメモリを解放すると、inactiveが無くなってスッキリした。


b.free

このようにbufferを定義した上で、SuperColliderでサウンドを処理する方法はいろいろある。 いちばん一般的なのは、「PlayBuf」というUGensのバラエティとして行う方法である。 まずは以下のように、SynthDefとして色々なパラメータを添えて「b」を"playbuf"という名前で定義しておく。 この状態ではまだ音は出ない。


(
	//.read brings in the whole sound at once
	b = Buffer.read(s,"sounds/a11wlk01.wav");
	SynthDef("playbuf",
		{
			arg out=0,
			bufnum=0,
			rate=1,
			trigger=1,
			startPos=0,
			loop=1;
			Out.ar(
				out,
				Pan2.ar(
					PlayBuf.ar(
						1,
						bufnum,
						BufRateScale.kr(bufnum)*rate,
						trigger,
						BufFrames.ir(bufnum)*startPos,
						loop
					),
					0.0
				)
			)
		}
	).add; 
)

そして以下のように呼び出せば、以前にも聞いたサウンドだが、 こんな音がした。 ここにはエンベロープが無くて無限リピートしているので、止めるには「コマンド+ピリオド」が必要である。


Synth(\playbuf, [\out, 0, \bufnum, b.bufnum]);

また、以下のように呼び出せば、再生のレートを0.5としたことで、「テープスピード1/2の再生」となり、1オクターブ下の再生となって こんな音がした。


Synth(\playbuf, [\out, 0, \bufnum, b.bufnum, \rate, 0.5]);

自分としてはSuperColliderをサウンドサーバとして走らせてOSCからパラメータを与え、 GUIのコントロールやセンサとのインタラクションはMax/MSP/jitterからOSCを出す、という構想であるが、 SuperColliderのGUIを使ってサンプルをライブにぐりぐりする事も可能である。 以下の例では、定番らしい「James' shortcut slider class」を使っている。 こんな音がした。


//Example with GUI controlling Synth 
(
	var w, rateslid, trigslid, startposslid, loopslid, a; 
	a=Synth(\playbuf, [\out, 0, \bufnum, b.bufnum]);
	w=Window("PlayBuf Example",Rect(10,200,300,150));
	w.front;
	w.view.decorator= FlowLayout(w.view.bounds);

	//James' shortcut slider class
	//100@24 means a Point of size 100 by 24
	//|ez| is the same as arg ez;  
	//	- the slider object is being passed into the callback action function
	rateslid= EZSlider(
		w,
		250@24,
		"Rate",
		ControlSpec(0.5, 10, 'exponential', 0.1),
			{
				|ez|
				a.set(\rate,ez.value)
			},
		1
	);
	trigslid= EZSlider(
		w,
		250@24,
		"Trigger",
		ControlSpec(0, 1, 'lin', 1),
			{
				|ez|
				a.set(\trigger,ez.value)
			},
		1
	);
	startposslid= EZSlider(
		w,
		250@24,
		"StartPos",
		ControlSpec(0.0, 1.0, 'lin', 0.01),
			{
				|ez|
				a.set(\startPos,ez.value)
			},
		0
	);
	loopslid= EZSlider(
		w,
		250@24,
		"Loop",
		ControlSpec(0, 1, 'lin', 0.1),
			{
				|ez|
				a.set(\loop,ez.value)
			},
		1
	);
	w.onClose_({a.free;});
)

次に出てくるのは「BufRd」である。これは上述の「PlayBuf」と似ているが、違いとしてはphaseというアーギュメントを用いて、 メモリを直接にアクセス出来るところである。 これはグラニュラシンセシスとかでは有効になってくるだろう。

以下の例では、マウスのX/Y座標でサウンドファイルをぐりぐりとスクラブできる。ライブDJものである。 ここで出てくる「K2A」は必須のもので、普段は低いサンプリングレートでマウスをスキャンしているのに対して、 BufRdはオーディオレートでサウンドサンプルをいじるので、これでオーディオレートにしているのである。 また、ここで出てくる「.lag」は「Lag UGen」のショートカットで、マウスのY座標に対して'catch-up delay'でスムージングをかけている。 こんな音がした。


(
	b = Buffer.read(s,"sounds/a11wlk01.wav");
	SynthDef("bufrd",
		{
			arg out=0,
			bufnum=0;
			Out.ar(
				out,
				Pan2.ar(
					BufRd.ar(
						1,
						bufnum,
						K2A.ar(
							BufFrames.ir(b.bufnum)
							*MouseX.kr(0.0,1.0)).lag(MouseY.kr(0.0,1.0)
						)
					),
					0.0
				)
			)
		}
	).play(s); 
)

このセクションの最後のトピックは「DiskIn」である。 これは、メモリにサウンドファイルの全体を一気に取り込めないような巨大なストリーミングサウンドに対して、 ストリーミングとしてメモリに次々に取り込むというものである。 ただし、これは再生にしか使えない(^_^;)。


(
	b=Buffer.cueSoundFile(s,"sounds/break",0, 1);
)

SynthDef(\diskin,{Out.ar(0,DiskIn.ar(1, b.bufnum))}).play(s);
このセクションに関しては、「Buffer」について、以下のようなヘルプも見るように、とある。

[DiskIn]
	help file- you will probably want to look at the 'Object 
	messaging style' further down the page, for now. 

[Index]
	buffer as array of data for UGen

[Osc]
	buffer as wavetable

[FFT]
	buffer as complex Fourier data, gets passed through 
	the phase vocoder processing chain

[Shaper]
	buffer for wave shaping distortion/complex sound generation
これで 7.1 Buffers and Sound Files.html は終わりである。 ここで、ちょうど午前のセッションの最後、Keynote 2が終わるところである。 このKeynote(Keynote Lecture 2: Adventures in Phy-gital Space [David Rokeby])は途中で退出する人が続出した。 デモビデオは多かったが、ちょっと中身が寂しかった模様である(^_^;)。 次は Workshop materials for Computer Music (G6002)7. Sound Synthesis 37.2 Granular Synthesis.html からとなる。 SuperColliderでのグラニュラは楽しみである(^_^)。

・・・上からたった1行であるが、このlunch breakでNIME国際組織委員会のミーティングがあった。 怒濤の英語のシャワーの中、僕はほとんど何も話すこともなく聞いているだけであったが、 およその話題はなんとか付いていけた。 NIME2012は米国ミシガン(ICMC1998で行った)、まだ未定ながらNIME2013はシンガポール(ICMC2003で行った)のセンで、 というような方向性である。 今後の開催地よりも、ICMCやシーグラフと連携して、同じ論文を複数の国際会議に投稿するケシカラン連中をどう阻止するか、 という話題の方が重要だった。

欧州に来たときにはいつもの事であるが、午後の時間帯(13-16時)が猛烈に眠くなる。 いま15時過ぎであるが、パソコンの時計を見れば、日本時間は22時過ぎである。 朝イチならSuperCollider日記も進むが、このコンディションではイマイチなので、 今日はここまで、と決心した。 今回はFTPでサーバに刻々と上げているので、ここまでを更新して、あとは午後のセッションから今後の研究ネタを拾い、 晩のコンサート(ホール/クラブ)を楽しむことにしよう。

2011年6月1日(水)

昨日は天気予報でもほぼ終日「雨」で、まさにユーミンの「冷たい雨」の世界であったが、 セッションもコンサートも相当に盛りだくさんで、ビールと共に、フルに楽しめた。 嬉しいニュースとしては、世界のComputer Music研究を引っ張り続けるスーパースター、Roger Dannenberg教授が隣に座ってきて、 国際会議ISMIRのコンサートセッションの審査員をして欲しい、と依頼されたこと(*^o^*)である。もちろん快諾。
そして、土日のプレNIME/openingに続く、NIME2011本体の会議期間としては最終日の朝となった。 この日は一転して快晴である。天気予報でも、明日の帰国日まで、もう傘は要らないようだ(^_^)。

さて、次のトピックは Workshop materials for Computer Music (G6002)7. Sound Synthesis 37.2 Granular Synthesis.html からとなる。 考えてみれば、Granular Synthesisというのは、僕のComputer Music研究歴のもっとも初期、 ここ(「情報処理学会誌」記事) とか、 ここ(共立出版bit別冊「コンピュータと音楽の世界」) とかで紹介したように、 ここの「研究業績」の1992年頃 あたりで親しんでいたものである。 最近では「粒状合成」という用語が提唱されているようであるが、「グラニュラ合成」の方がしっくり来る。 ここでも、以下のように、サーバを「s」として特定するために、ボタンでなくpost window内からlocalhost serverを起動しておく。


Server.default=s=Server.local;
s.boot;
グラニュラシンセシスを簡単に言うと、「In Granular Synthesis sounds are modelled out of microscopic particles of sound, short grains in the region of 10-100 milliseconds long.」ということになる(^_^;)。 サウンドを連続した信号として扱わずに、サウンドに10ミリ秒から100ミリ秒程度のエンベロープ(スムースに立ち上がって持続してスムースに減衰する)をかけた「グレイン(粒)」を用意して、これを非常に多数、時間的・空間的に配置する、というものである。 人間の聴覚はこれを聞くと、連続したサウンドの「雲」のように知覚する。

グレインの選択には無限の可能性があるが、もっともシンプルなものとしては、以下のように、 それぞれグレインとなるようにエンベロープをかけた「サイン波形」「任意のサンブルサウンド(自然音)」などがある。


b = Buffer.read(s,"sounds/a11wlk01.wav");

//three different possible grains 
(
	{
		var singrain1, singrain2, sfgrain;
		singrain1=SinOsc.ar(440,0,XLine.kr(1.0, 0.0001, 0.05));
		singrain2=FSinOsc.ar(800, 0.0, Line.kr(1.0,0,0.05).squared);
		sfgrain=(
			PlayBuf.ar(1,b.bufnum, BufRateScale.kr(b.bufnum)))
			*EnvGen.kr(Env([0,1,1,0],[0.01,0.01,0.01], -4)
		);
		[singrain1,singrain2,sfgrain]
	}.plot(0.1,s)
)

このグレインを多数ばらまくと(周期的な配置だとそのリズムとかビートとかピッチ関係が知覚されてしまうので、基本的にランダムにばらまく事個が重要)、「we can make macroscopic soundscapes.」ということで、我々は全体としてのサウンドを知覚できる。 そのためにSuperColliderで必要なのは、ちょうど前のトピックで学んだスケジューリングのテクニックである。 まず、グレインを以下のように定義しておく。


(
	SynthDef(\sinegrain,
		{
			arg pan,freq, amp;
			var grain; 
			grain= SinOsc.ar(freq, 0, amp)
				*(XLine.kr(1.001,0.001,0.1,doneAction:2)-0.001);
			Out.ar(0,Pan2.ar(grain, pan))
		}
	).add; 
)
まずは、この定義されたグレインを1個だけ再生する。 これはGranular Synthesisとは言えない(^_^;)が、 こんな音がした。 ちなみにrandomを使っているので、呼び出したたびに違った音になる。

Synth(\sinegrain,
	[
		\freq,
		rrand(100,10000),
		\amp,
		exprand(0.05,0.1),
		\pan,
		1.0.rand2
	]
);

そして、「10秒間の期間にランダムなパラメータの1000個のグレインを呼び出す」とすると、 ようやくGranular Synthesisらしい、 こんな音がした。 ただし、ここではまだ「0.01秒ごと」の等間隔に呼び出しているので、そのつもりで聞くと「グレインの繰り返されるビート」が知覚されるので、 完全な意味でのGranular Synthesisではない。


(
	{
		1000.do
			{
				arg i; 
				Synth(\sinegrain,
					[
						\freq,
						rrand(100,10000),
						\amp,
						exprand(0.05,0.1),
						\pan,
						1.0.rand2
					]
				);  
				0.01.wait
			}; 
	}.fork
)

このようなサウンドはMax/MSPでも簡単に作れるが、全体としてststicなのであまり面白くはない(^_^;)。 面白くなってくるのは、個々のGrainに与えるパラメータやGrainの密度など、 より高次のメタ情報を時間的に変化させるところからである。 以下の例では、グレインの総数を1000個として、 それらが呼び出される時間間隔が次第に大きく(密度が次第に小さく)なるように変化させている。 こんな音がした。


(
	{
		1000.do
			{
				arg i; 
				var timeprop = (i/199.0)**3;
				Synth(\sinegrain,
					[
						\freq,
						exprand(100,5000-(20*i)),
						\amp,
						exprand(0.05,0.1),
						\pan,
						1.0.rand2
					]
				);  
				rrand((timeprop*0.1).max(0.01),timeprop*0.3).wait
			}; 
	}.fork
)

上の例ではグレインの中身がサイン信号だったが、次は、生サウンドにGrainのエンベロープを乗算した「Granular Sampling」である。 以下の例では、サウンドファイルを読み出してGrain化しているが、サウンド入力をライブサンプリングしてGrain化して、 そのままGranular Synthesisで生成すれば、一種のライブエフェクトとしてかなり「使える」サウンドとなる。 まず、以下はサウンドバッファのGrain化の定義と、1つだけの再生の例である。 こんな音がした。 こちらも呼び出したたびに違った音になる。


(
	SynthDef(\sfgrain,
		{
			arg bufnum=0,
			pan=0.0,
			startPos=0.0,
			amp=0.1,
			dur=0.04; 
			var grain; 
			grain= PlayBuf.ar(
				1,
				bufnum,
				BufRateScale.kr(bufnum),
				1,
				BufFrames.ir(bufnum)*startPos,
				0
			)*(
				EnvGen.kr(
					Env.perc(0.01,dur),
					doneAction:2)-0.001
			);
			Out.ar(0,Pan2.ar(grain, pan))
		}
	).add; 
)

b = Buffer.read(s,"sounds/a11wlk01.wav");

//individual grain
Synth(\sfgrain,
	[
		\bufnum,
		b.bufnum,
		\startPos,
		rrand(0.0,1.0),
		\amp,
		exprand(0.005,0.1),
		\pan,
		1.0.rand2
	]
);  

以下の例でも、グレインの総数を1000個として、 個々のグレインのサンプルスタートポイントをランダムに変化させながら、 それらが呼び出される時間間隔が次第に大きく(密度が次第に小さく)なるように変化させている。 こんな音がした。


(
	{
		200.do
			{
				arg i; 
				var timeprop = (i/199.0)**3;
				Synth(\sfgrain,
					[
						\bufnum,
						b.bufnum,
						\startPos,
						rrand(0.0,timeprop),
						\amp,
						exprand(0.005,0.1),
						\pan,
						1.0.rand2
					]
				);  
				rrand(
					(timeprop*0.1).max(0.01),
					timeprop*0.4).wait
			}; 
	}.fork
)

既に書いていたことだが、ここに以下のような解説があった。 3つ目の段落にあるように、このアイデアはクセナキスによって1950年代に提起され、 1970年代にBarry Truaxによってコンピュータ上で実現された。 ライブサンプリングのGranular Samplingは1980年代に実現されたが、 この時にはPC上ではとうてい無理で、1000万円以上のシンクラビアで実装された。

Each grain might have many different parameters attached to it; some salient 
ones might be the pitch, the duration of the envelope, the pan position in the 
stereo field or the amplitude. The overall cloud can also have some sort of 
distribution for these parameters, which might lead to a tendency mask 
determining the range of frequencies of the particles allowed at differnet 
points in time, or control of the evolving density of the cloud. 

The composer's work is to both specify the grains, and also control how they 
are used over time to make an interesting compositional structure. 

These techniques were first conceptualised and explored in instrumental and 
electronic music by Iannis Xenakis (late 50s), and further investigated in 
computer implementation, notably by Curtis Roads and Barry Truax, from the 
early 1970s on. Real-time systems became plausible in the 1980s.  
既に書いていたことだが、さらに以下のような解説があった(^_^;)。
Because you can take tiny slices of sound, granular processing allows 
one to perform quite dramatic transformations on sound sources. 
The sound can be made to disappear into a stream of tiny quanta and 
reappear, coalescing out of distinct particles:
そして最後の例として、以下はGUIを使って、Granular Synthesisの(広域)パタメータをぐりぐり、である。 スライダの位置が停止していると等間隔の発音でのピートが生まれてしまうが、スライダを操作すると面白くなる。 ただしGrain化のためのエンベロープの立ち上がり部分が急峻なのがちょっと気になる。 こんな音がした。

(
	var w, slid, lastval;
	lastval=0.0;
	w=Window("My Window", Rect(100,500,200,200)); 
		//A 200 by 200 window appears at screen co-ordinates (100, 500)
	slid=Slider(w,Rect(10,10,150,40));
	slid.action_({lastval= slid.value;});
	{
		inf.do
			{
				arg i; 
				var prop, timestart, timeend;
				prop= (i%300)/300;
				timestart= prop*0.8;
				timeend= prop*(0.8+(0.1*lastval));
				Synth(\sfgrain,
					[
						\bufnum,
						b.bufnum,
						\startPos,
						rrand(timestart,timeend),
						\amp,
						exprand(0.005,0.1),
						\pan,
						lastval.rand2,
						\dur,
						0.1+(lastval*0.5)
					]
				);
					// max in this to avoid ever going near 0.0 wait time, 
					// which would crash the computer!
				(((lastval*0.2)+0.01).max(0.01)).wait
			}; 
	}.fork;
	w.front;
)

・・・ここまでで、午前のpaper sessionの前半が終わってcoffee breakとなった。 ワークショップのページのサンプルはここまでであったが、さらにヘルプで「PitchShift」とか「TGrains」とある。 試しにpost windowに「PitchShift」を置いてハイライトさせ、メニューからHelpを呼ぶと、以下のような解説があった。

PitchShift

PitchShift.ar(in, windowSize, pitchRatio, pitchDispersion, timeDispersion, mul, add)

A time domain granular pitch shifter.
Grains have a triangular amplitude envelope and an overlap of 4:1.
in - the input signal.
windowSize - the size of the grain window in seconds. This value cannot be modulated.
pitchRatio - the ratio of the pitch shift. Must be from 0.0 to 4.0.
pitchDispersion - the maximum random deviation of the pitch from the pitchRatio.
timeDispersion - a random offset of from zero to timeDispersion seconds is added to the delay
of each grain. Use of some dispersion can alleviate a hard comb filter effect due to uniform
grain placement. It can also be an effect in itself. timeDispersion can be no larger than windowSize.

(
play({
	z = Blip.ar(800, 6, 0.1);
	PitchShift.ar(z, 0.02, Line.kr(0.1,4,20), 0, 0.0001)
}))

(
// pitch shift input - USE HEADPHONES to prevent feedback.
play({
	PitchShift.ar(
		SoundIn.ar([0,1]),	// stereo audio input
		0.1, 			// grain size
		MouseX.kr(0,2),	// mouse x controls pitch shift ratio
		0, 				// pitch dispersion
		0.004			// time dispersion
	)
}))

(
// use PitchShift to granulate input - USE HEADPHONES to prevent feedback.
// upper left corner is normal playback. x = pitch dispersion, y = time dispersion
var grainSize;
grainSize = 0.5;
play({
	PitchShift.ar(
		SoundIn.ar([0,1]), 
		grainSize, 		
		1,						// nominal pitch rate = 1
		MouseX.kr(0,1), 			// pitch dispersion
		MouseY.kr(0, grainSize)	// time dispersion
	)
}))
午前の後半のpaper sessionの内職で(^_^;)、ここをさらに実験してみることにした。 まずは以下の、ピッチシフトの最初の例である。 シンプルなサウンドを鳴らし、これを20秒間かけて、ずんずんと上げる方向にピッチシフトしている。 こんな音がした。

(
	play(
		{
			z = Blip.ar(800, 6, 0.1);
			PitchShift.ar(z, 0.02, Line.kr(0.1,4,20), 0, 0.0001)
		}
	)
)

そして以下の例では、ライブのサウンド入力をマウスのX座標でライブにピッチシフトしている。 poster sessionの質疑応答のサウンドがソースである(^_^;)。 この例ではヘッドホンをしないとハウリングが起きてしまう。 こんな音がした。


(
	// pitch shift input - USE HEADPHONES to prevent feedback.
	play(
		{
			PitchShift.ar(
				SoundIn.ar([0,1]),	// stereo audio input
				0.1, 			// grain size
				MouseX.kr(0,2),	// mouse x controls pitch shift ratio
				0, 				// pitch dispersion
				0.004			// time dispersion
			)
		}
	)
)

そして以下の例では、ライブサンブリングのサウンド入力をGrain化してGranular Synthesisで再生し、マウスのX/Y座標でライブにピッチシフト/Grain濃度変化させている。 poster sessionの講演のサウンドがソースである(^_^;)。 この例でもヘッドホンをしないとハウリングが起きてしまう。 こんな音がした。


(
	// use PitchShift to granulate input - USE HEADPHONES to prevent feedback.
	// upper left corner is normal playback. x = pitch dispersion, y = time dispersion
	var grainSize;
	grainSize = 0.5;
	play(
		{
			PitchShift.ar(
				SoundIn.ar([0,1]), 
				grainSize, 		
				1,		// nominal pitch rate = 1
				MouseX.kr(0,1), 	// pitch dispersion
				MouseY.kr(0, grainSize)	// time dispersion
			)
		}
	)
)

Granular Samplingに関するヘルプが終わったが、まだsessionは続いているので、「TGrains」というヘルプも引いてみた。 まず、定義は以下である。 Granular SynthesisをTGenのように使う、ということだろう。

TGrains	buffer granulator

Triggers generate grains from a single channel (mono) buffer. 
Each grain has a Hann envelope (sin^2(x) for x from 0 to pi) 
and is panned between two channels of multiple outputs.

TGrains.ar(numChannels, trigger, bufnum, rate, centerPos, dur, pan, amp, interp)

	numChannels - number of output channels.
	trigger - at each trigger, the following arguments are sampled and used 
		as the arguments of a new grain. 
		A trigger occurs when a signal changes from <= 0 to > 0.
		If the trigger is audio rate then the grains will start with sample accuracy.
	bufnum - the index of the buffer to use. It must be a one channel (mono) buffer.
	rate   -  1.0 is normal, 2.0 is one octave up, 0.5 is one octave down
			-1.0 is backwards normal rate ... etc.
		Unlike PlayBuf, the rate is multiplied by BufRate, so you needn't do that yourself.
	centerPos - the position in the buffer in seconds at which the grain envelope will reach 
			maximum amplitude.
	dur    -   duration of the grain in seconds.
	pan    -   a value from -1 to 1. Determines where to pan the output in the same manner as PanAz.
	amp   - amplitude of the grain.
	interp - 1,2,or 4. Determines whether the grain uses (1) no interpolation, (2) linear interpolation, 
			or (4) cubic interpolation.
最初の例では、1行目のようにサウンドファイルを指定しておいて(これは以降も有効)、マウスのX/Y座標によって、 Granular Samplingの発音パラメータを変化させる。 こんな音がした。

s.sendMsg(\b_allocRead, 10, "sounds/a11wlk01.wav");

(
	{
		var b = 10, trate, dur, rate;
		trate = MouseY.kr(2,200,1);
		dur = 4 / trate;
		rate = Dseq([10, 1, 1, 0.5, 0.5, 0.2, 0.1], inf);
		TGrains.ar(
			2,
			Impulse.ar(trate),
			b,
			rate,
			MouseX.kr(0,BufDur.kr(b)),
			dur,
			Dseq([-1, 1], inf),
			0.1,
			2
		);
	}.play;
)

以下の例では、マウスのX/Y座標により、Grainのdurationとサンブル再生ポイントを変化させる。 こんな音がした。


(
	{
		var b = 10, trate, dur, clk, pos, pan;
		trate = MouseY.kr(8,120,1);
		dur = 12 / trate;
		clk = Impulse.kr(trate);
		pos = MouseX.kr(0,BufDur.kr(b)) + TRand.kr(0, 0.01, clk);
		pan = WhiteNoise.kr(0.6);
		TGrains.ar(2, clk, b, 1, pos, dur, pan, 0.1);
	}.play;
)

以下の例では、マウスのX/Y座標により、Grainのdurationとサンブル再生ポイントを変化させるというのは同じだが、パラメータの作用のさせ方が違っている。ちょっとした変更で大きくテイストが変わるのが面白い。 こんな音がした。


(
	{
		var b = 10, trate, dur, clk, pos, pan;
		trate = MouseY.kr(8,120,1);
		dur = 12 / trate;
		clk = Impulse.kr(trate);
		pos = MouseX.kr(0,BufDur.kr(b)) + TRand.kr(0, 0.01, clk);
		pan = WhiteNoise.kr(0.6);
		TGrains.ar(4, clk, b, 1, pos, dur, pan, 0.1);
	}.play;
)

ここで午前のsessionが終わってlunch breakとなった。 NIMEのpaper sessionはこれで終わり、あとは午後にposter/demoセッション、Keynote 3、そしてNIME meetingがあり、 その後はホールコンサートとクラブコンサート(終了は連日の24時頃)である。 清々しい風を楽しみつつ構内をちょっと散歩してから戻り、この部分はオスロ大学図書館のロビーで続けている。 以下の例では、マウスのX/Y座標により、Grainのdurationとサンブル再生ポイントを変化させるというのは同じだが、パラメータの作用のさせ方がまた違っている。 こんな音がした。


(
	{
		var b = 10, trate, dur, clk, pos, pan;
		trate = MouseY.kr(8,120,1);
		dur = 4 / trate;
		clk = Dust.kr(trate);
		pos = MouseX.kr(0,BufDur.kr(b)) + TRand.kr(0, 0.01, clk);
		pan = WhiteNoise.kr(0.6);
		TGrains.ar(2, clk, b, 1, pos, dur, pan, 0.1);
	}.play;
)

以下の例もまた別のテイストになっている。 こんな音がした。


(
	{
		var b = 10, trate, dur, clk, pos, pan;
		trate = LinExp.kr(LFTri.kr(MouseY.kr(0.1,2,1)),-1,1,8,120);
		dur = 12 / trate;
		clk = Impulse.ar(trate);
		pos = MouseX.kr(0,BufDur.kr(b));
		pan = WhiteNoise.kr(0.6);
		TGrains.ar(2, clk, b, 1, pos, dur, pan, 0.1);
	}.play;
)

以下の例もまた別のテイストになっている。 こんな音がした。


(
	{
		var b = 10, trate, dur, clk, pos, pan;
		trate = 12;
		dur = MouseY.kr(0.2,24,1) / trate;
		clk = Impulse.kr(trate);
		pos = MouseX.kr(0,BufDur.kr(b)) + TRand.kr(0, 0.01, clk);
		pan = WhiteNoise.kr(0.6);
		TGrains.ar(2, clk, b, 1, pos, dur, pan, 0.1);
	}.play;
)

以下、まだ3つほどサンプルが並んでいる。それぞれ面白い音がするが、ちょっと省略する(^_^;)。


(
	{
		var b = 10, trate, dur, clk, pos, pan;
		trate = 100;
		dur = 8 / trate;
		clk = Impulse.kr(trate);
		pos = Integrator.kr(BrownNoise.kr(0.001));
		pan = WhiteNoise.kr(0.6);
		TGrains.ar(2, clk, b, 1, pos, dur, pan, 0.1);
	}.play;
)

(
	{
		var b = 10, trate, dur, clk, pos, pan;
		trate = MouseY.kr(1,400,1);
		dur = 8 / trate;
		clk = Impulse.kr(trate);
		pos = MouseX.kr(0,BufDur.kr(b));
		pan = WhiteNoise.kr(0.8);
		TGrains.ar(2, clk, b, 2 ** WhiteNoise.kr(2), pos, dur, pan, 0.1);
	}.play;
)

(
	{
		var b = 10, trate, dur;
		trate = MouseY.kr(2,120,1);
		dur = 1.2 / trate;
		TGrains.ar(
			2,
			Impulse.ar(trate),
			b,
			(1.2 ** WhiteNoise.kr(3).round(1)),
			MouseX.kr(0,BufDur.kr(b)),
			dur,
			WhiteNoise.kr(0.6),
			0.1
		);
	}.play;
)
以下の例がここでの最後のサンプルである。何やら不思議な反応をしていて、パッと見には詳細不明であった(^_^;)。

(
	{
		var trate, dur, z, d;
		trate = MouseX.kr(1, 100, 1);
		d = { Dwhite(0.1, 0.2, 1) };
		z = { 
			Drand([
				Dgeom(
					0.1,
					1 + d.value,
					Diwhite(20, 40)),
					Dgeom(1, 1 - d.value, Diwhite(20, 40)
				)]
			) 
		};
		TGrains.ar(
			2, 
			Impulse.ar(trate),
			bufnum: 10, 
			rate: Dseq([1, 1, z.value, 0.5, 0.5, 0.2, 0.1, 0.1, 0.1, 0.1], inf) * 2 + 1, 
			centerPos: Dseq(z.dup(8), inf),
			dur: Dseq([1, d.value, 1, z.value, 0.5, 0.5, 0.1, z.value] * 2, inf) / trate,
			pan: Dseq([1, 1, 1, 0.5, 0.2, 0.1, 0, 0, 0], inf) * 2 - 1,
			amp: Dseq([1, 0, z.value, 0, 2, 1.0, 1, 0.1, 0.1], inf)
		);
	}.play;
)
・・・ここでようやく、 Workshop materials for Computer Music (G6002)7. Sound Synthesis 37.2 Granular Synthesis.html が終わった。ここだけはヘルプまで深入りしてみた。次は 7.3 GUI+Loop Example.html からとなる。

ちょうど午後のposter/demo sessionの時間帯となったので、ここでいったんの区切りであるが、ぼちぼち眠い時間帯となるので、 今日は潔くここまでとして、サーバに上げておくことにした。 共用WiFiからの140MBのFTPなので、相当に時間がかかりそうである。 1階ロビーでFTPを開始して、その後、3階のposter/demo session会場までMacBookAirを抱えて移動したがWiFiは切れず、 ザッと眺めてから1階に下りてセッション会場のAuditoriumに入っても順調にFTPは続いた。大したものだ。(^_^)

2011年6月3日(金)

NIMEが6月1日(の24時近く(^_^;))に終わり、ホテルに戻ったらもう現地時間でも6月2日になっていた。 そこから何時間か寝て9時過ぎにホテルをチェックアウトしたが、帰国のフライトは夕方の18時発、ということで、 オスロ最終日はほぼ半日の観光日となった。 オスロ18:05→ミュンヘン20:20のルフトハンザから乗り継ぎの成田へのANAはなんと20:55発である。 たった25分という短時間の乗り継ぎはこれまで経験が無い「危ない」スケジュールであるが、 まぁ「行き」でなくて「帰り」なので、フライトが遅れて乗り継ぎ便に取り残された場合には、 航空会社の手配したホテルでもう1泊するだけ。 ANAのWebでまとめて予約しているので、乗り継ぎ出来ないフライトは発券されない筈なのである。

・・・で帰国日の6月2日は、フィヨルドを巡るクルーズに乗ったりしたが、ここでは詳細は省略。 ホテルに戻って空港行きの電車に乗るためにオスロ中央駅行きのトラム(路面電車)に乗ったら、 なんとあと1駅のところで故障して全員降りろ、という事になってスーツケースを150mほどごろごろ押したが、 時間は超ゆったりだったのでオスロ空港ではビールとともにメイルをチェックできた。 オスロを10分ほど遅れて出発したフライトは機長がガンガン飛ばして定刻に到着すると、 ミュンヘン空港では「成田行きのお客様」というボードを持った地上職員が待ち構えていた。 このスタッフとサクサク歩いて無事に成田行きに間に合い、いまこれを書いているのは出発した直後、 日本時間では午前5時前である。あと11時間かぁ(^_^;)。

なかなかSuperColliderに入らないが備忘録としてメモしておくと、フィヨルドクルーズの船内でビール(11時)。 その後、王宮に行くのを断念して公園のカフェでワイン1杯(僕はワインと言えば「赤」)でまったり(13時)。 オスロ空港に早めに着いてゲート前のカフェでビール大(16時)。 オスロ→ミュンヘンの機内でワインを2杯(気取らないが量は十分、19時)。 そしてANA機内で、夕食前のドリンクにクウォーターのワインを1本(22時[現地]、既に空いた)。 これから夕食では、いつものようにクウォーターのワインを2本頼むので、これで果たしてどれだけ飲んだのかなぁ(^_^;)。

・・・上からたった1行のジャンブであるが、機内では6時間以上が経過した。 いま日本時間は10時半、フライトはあと4時間ちょっとである。 ルフトハンザから合計すれば確実にボトル1本分を越えるワインとともに爆睡していたところから目覚め、 機内で深呼吸と入念な全身ストレッチとともに冷たいオレンジジュースを2杯、そしてコーヒー。 この全身に血流が蘇る瞬間が、最近の海外出張の一番の楽しみである(^_^;)。 周囲は死に絶えたように寝ているが、これで帰国すると夜に眠れない(昼間に眠い)日が続くのである。

さて、続きは Workshop materials for Computer Music (G6002)7. Sound Synthesis 37.3 GUI+Loop Example.html からとなる。 ここはサンブルが1つ置かれているだけで、あれこれ解説が無い。 こういう、「サンブルから好きに学べ」というスタイルもSuperColliderらしい(^_^)。 「In this example code, a sound file can be made to loop at any selection in the graphical display」 ということなので、タイトル通りにGUIによりサウンドファイルから好きに選んだところをループさせる、という事である。 Max/MSPであれば"groove"オブジェクトのサンブルかtutorialにあったやつである。

まず、これまでと同様のlocalhost serverの起動とサウンド素材ファイルの指定、そして今回のSynthDefは以下である。


Server.default=s=Server.local;
s.boot;

b = Buffer.read(s,"sounds/a11wlk01.wav");

c= SynthDef(\loopbuffer, 
	{
		arg start=0,
		end=10000;
		Out.ar(
			0,
			Pan2.ar(
				BufRd.ar(
					1,
					0,
					Phasor.ar(
						0,
						BufRateScale.kr(b.bufnum),
						start,
						end
					),
					0.0
				)
			)
		)
	}
).play(s);
// *BufFrames.ir(b.bufnum)
// this wasn't needed since the GUI 
// gives us positions directly in samples
その上で、今回のサンブルの大事な部分は以下である。 まさにDJ気分で、波形の好きなところを指定してループでき、選択部分をドラッグすればループ幅を変えずに中身をスライド出来る。 こんな音がした。

(
	w = SCWindow.new("soundfiles looper", Rect(10, 700, 750, 100));
	w.front;
	a = SCSoundFileView.new(w, Rect(20,20, 700, 60));
	f = SoundFile.new;
	f.openRead("sounds/a11wlk01.wav");
	a.soundfile = f; // set soundfile
	a.read(0, f.numFrames); // read in the entire file.
	a.refresh; // refresh to display the file.

	//set a function which is called when the mouse 
	//is let go, i.e. just after dragging out a 
	//selection in the window
	a.mouseUpAction_(
		{
			arg view; 
			var where;
			where= (view.selections[0]);
			//get the latest selection 
			//(assuming no other selections going on)
			where.postln; 
			c.set(
				\start,where[0],
				\end,
				where[0]+where[1]);
				//convert to start and end for loop
		}
	);
)

これでアッサリと終わりとなったので、続きは Workshop materials for Computer Music (G6002)7. Sound Synthesis 37.4 Plotting.html である。 タイトルは「Plotting with SC」である。 SuperColliderでサウンドをビジュアライズする手法としては、既に以下を扱っている。

SuperColliderのplot機能を調べるためには、postウインドウで「plot」と打ってこれをハイライトさせた状態で「cmd+Y」とすると、 以下のように出て来た。 このplotを活用すると、サウンド合成とか信号処理全体のダイアグラムを確認できる。

Implementations of 'plot' :
   ArrayedCollection:plot :     this.plot(name, bounds, discrete, numChannels, minval, maxval, parent, labels)
   Buffer:plot :     this.plot(name, bounds, minval, maxval, parent, labels)
   Env:plot :     this.plot(size, bounds, minval, maxval, parent)
   Function:plot :     this.plot(duration, server, bounds, minval, maxval, parent, labels)
   Interpl:plot :     this.plot
   InterplEnv:plot :     this.plot(size)
   SCImage:plot :     this.plot(name, bounds, freeOnClose, background, showInfo)
   SoundFile:plot :     this.plot(bounds)
   Wavetable:plot :     this.plot(name, bounds, minval, maxval, parent, labels)
localhost serverが起動している状態で、以下の例は100Hzの三角波を1周期だけplotしている。

{LFTri.ar(100)}.plot(0.01)

以下の例は、FMの様子のクローズアップを見ている。


{
	SinOsc.ar(400+SinOsc.ar(100,0,200))
}.plot(0.025)

以下の例は、ステレオのFMの様子を遠くから見ている。


{
	SinOsc.ar(400+SinOsc.ar([10,100],0,[200,100]))
}.plot(0.2)

以下の例は、エンベローブの形状を見ている。


Env([0,1,1,0],[0.5,1.0,2.0],[10,0,-4]).plot

以下の例は、「InterplEnv」のエンベローブの形状を見ている。


i= InterplEnv([0,1,1,0],[0.5,1.0,2.0],[10,0,-4]).plot

この「InterplEnv」は「IEnvGen」に使われるもので、以下の例のように、メモリの割り当てを必要とするUGenを置き換えることが出来るが、 ちゃんと連続値を与えるという。


{
	SinOsc.ar(
		IEnvGen.kr(
			i,
			MouseX.kr(0,3.5)
		)*500+200,
		0,
		0.2
	)
}.play
以下の例は、ArrayedCollectionを見ている。 ある値から次の値までを線形に繋いでくれるわけである。

[0,5,1,3,2,4].plot

以下の例は、サウンドバッファ(サンブル)をplotしている。鳴っている音はいつものやつなので省略(^_^;)。


Server.default=s=Server.local;
s.boot;

b=Buffer.read(s,"sounds/a11wlk01.wav"); 

b.plot

{PlayBuf.ar(1,b)}.play

以下の例は、「language」にするための、より安全なサンブルだという。見た目は同じだが(^_^;)、Bufferの様子がキチンとするという事か。


Server.default=s=Server.local;
s.boot;

b=Buffer.read(s,"sounds/a11wlk01.wav"); 

b.loadToFloatArray(
	0,
	-1,
	{
		arg data;
		a=data;
	}
)

a.plot

ここでバッテリが25%ほどになったので、途中もいいところだがここまでとする。続きはいつになるか。(^_^;)

2011年6月7日(火)

帰国しての土日には、出張報告書類(清算とか大変)、そして2000枚以上の写真を並べた NIME2011レポート などを作っていたら終わった。 そして月曜日には「音楽情報科学」とM1指導、火曜日には「サウンドデザイン」と生産学生アポとM2指導、と追われるいつもの日々。 放課後には、インスタレーション作品「はやくスシになりたい」の作者、卒業生の野口さんが研究室に来て、 7月にSUACギャラリーで開催する彼女の個展の打ち合わせで、 サポートプロジェク志願の1回生たちも1106研究室にやって来る。

つかの間の午後の1時間ほどだが、ちょっとだけでも進めていこう。 Workshop materials for Computer Music (G6002)7. Sound Synthesis 37.4 Plotting.html の続きである。 以下の例は、「manipulate using language」とある。 読み込んだ波形をFloatArrayに入れて、さらに乱数で再度サンブリングしているが、見た目は変わらない(^_^;)。


Server.default=s=Server.local;
s.boot;

b=Buffer.read(s,"sounds/a11wlk01.wav"); 

b.loadToFloatArray(
	0,
	-1,
	{
		arg data;
		a=data;
	}
)

a= a.collect{|val| if(0.1.coin,{val*val},{val*val*val}) }; 

a.plot

そして最後に、以下のように「return to buffer」としている。 見た目は変わらないが、波形データでも配列でも同等に処理できる、という事だろうか。


b.loadCollection(a); 

b.plot

次はWaveTableである。 これは「fill」メソッドで以下のように生成できる。


a= Signal.fill(
	256,
		{
			arg i;
			var t= (i/255.0)+0.1.rand;
			(t*t)-t+(0.3*t*t*t)
		}
); 

a= a.asWavetable

a.size

a.plot

このWavetableをパッファ「b」にロードして表示したのが以下である。


b=Buffer.alloc(s,512,1);

b.loadCollection(a);

b.plot

これを繰り返し波形として再生すると、以下のように、 こんな音がした。


{Osc.ar(b,440,0,0.2)}.play

また、のブザー音のようなサウンドに、以下のように、マウス操作によってフィルターをかけると、 こんな音がした。 ただしここではエイリアスノイズに対して何も保護していないので、音は良くない(^_^;)。


{
	LPF.ar(
		Osc.ar(
			b,
			MouseX.kr(50,500,'exponential'),
			0,
			0.2
		),
		MouseY.kr(100,10000,'exponential')
	)
}.play 

次に以下は、「SoundFile」クラスによって波形を読み込んで表示させる例である。 「SCSoundFileView」を使って表示してくれる。


f= SoundFile.openRead("sounds/a11wlk01.wav");

f.plot //uses the SCSoundFileView

その他のグラフィック関数としては、以下のヒストグラムがある。


Array.rand(1000,0.0,1.0).histo(10,0,1).plot(minval:0,maxval:200) 

最後の例として、以下がある。20本の波形を同時にplotしている。


a = 20.collect{|x|20.collect{|y| (x+y/pi).sin.rand}}.flat;

a.plot(numChannels:20);

ここに、既にチラッと出てパスしていた「Extensions folder/Quarks」がインストールされていると、 以下の「面白い」heatMapというのが表示できるらしいが、ここではパスするしかない。(^_^;)


a.heatMap(20); // Much better!

// Choose your colour scheme:
a.heatMap(20, colscheme: \red);
a.heatMap(20, colscheme: \brw);
a.heatMap(20, colscheme: \coals);
これで、 Workshop materials for Computer Music (G6002)7. Sound Synthesis 3 が終わったことになる。 次は 8. Effects Processing8.1 Nodes.html からである。 ぼちぼち視界の下の方に、「9. Algorithmic Composition」が、 そしてその次の「10. Network Music」のところには「10.1 Open Sound Control.html」というのが見えてきた。 Processing の方でもトピックがOSCに差し掛かっていて、いよいよMax/MSP/jitterを橋渡しとして、 SuperColliderとProcessingとが連携するのも近い、と期待させるところである。 ここまでを区切りとして、次は「SuperCollider日記(5)」として進めることにしよう。

SuperCollider日記(5)