SuperCollider日記(5)

長嶋 洋一

(一部 Processing日記かも)

Part 1  Part 2  Part 3  Part 4  Part 6  Part 7 


2011年7月28日(木)

前回のSuperCollider日記が6月7日、オスロでの NIME2011 から帰国したところだったので、なんと2ヶ月近く、進展が無いまま放置してしまった。 そして明後日には、 ICMC2011 での発表のためにイギリスに向かう。 海外出張とかで日々の些事から途絶しないと、なかなかこの手の勉強は進まないものだ。

ただし、その期間に、 時間学会 に行き、毎週火曜日の放課後には 野口佳恵・個展 のいろいろな準備があり、 M2の見崎さんの修了制作に向けての製作や4回生の総合演習IIのインスタレーションのための製作が、 と続き、 野口佳恵「けだもの展」 を無事に開催し、 直前追い込み の末に 「メディア造形総合演習II」最終合評 を無事に終えたので、サボッていたということでもない。 学生たちの作品は、改訂した ここ に記載した。

今回の ICMC2011 では、コンサートでなくポスターセッションでの発表であり、既にポスターは 採択されたペーパー を4枚のA3に奇麗にプリントしたので、これを持っていき、パソコンで ロシアでの公演映像 でも見せればOK・・・とかなり気楽に構えている(^_^;)。 セントレアから成田に飛び、そこからロンドンに飛び、さらにマンチェスターに飛び、そこから電車でHuddersfieldという街に行く、 ということで、初めての場所なのに何も準備していない。 まぁ、なんとかなるだろう。 そしてせっかくなので、この機内その他(日常と違うテンションの場)で、久しぶりにSuperColliderの勉強を再開しよう、という事である。

進展はもうすっかり忘れてしまっていたが、前回までで、 Workshop materials for Computer Music (G6002)7. Sound Synthesis 3 が終わっている。 そこで次は 8. Effects Processing8.1 Nodes.html からである。 まず、いつものように、


Server.default=s=Server.internal;
とinternal serverを「s」という名前で起動しておく。 ここでのトピックは、SuperColliderがバックグラウンドで行う信号処理プロセスのスケジューリングである。 用語として「Node」という概念が登場する。これは、SuperColliderのServerは、走っているSynthsをツリー状のグラフとして管理するので、 そのそれぞれのSynthsをNodeと言う。 現在のNodesの状態は、以下のように「 s.queryAllNodes 」と入れれば表示される。

また、現在のNodesの状態は、サーバウインドウが「on focus」すなわち選択されている時に「N」を押すと、 以下のように表示される。

SuperColliderのサーバがサウンド(Audio Out)を決定していくためには、 全てのSynthのNodesをグラフとして辿ってレンダリングする必要がある。 そのグラフを辿る順番が、それぞれのSynthプロセスの実行の順番となる。 例えばサウンドにリバーブのエフェクトをかけるためには、その入力にあたるサウンドを崎に処理しておく必要がある。

テキストのDemoサンプルはマイク入力に対するリバーブなのだが、お仕事パソコンのMac miniにはマイクが無いので、 過去にあったサウンド生成サンプル(twitterの長さのNo.2「02 LFSaw」)をソースとしてみると、 こんな音がした。


(
	s=Server.default;
	SynthDef("reverb",{Out.ar(0,CombN.ar(
		Splay.ar(
			Ringz.ar(
				Impulse.ar(
					[2, 1, 4], [0.1, 0.11, 0.12]
				),
				[0.1, 0.1, 0.5]
			)
		) 
		* EnvGen.kr(
			Env(
				[1, 1, 0], [120, 10]
			),
			doneAction: 2
		)
		,0.1,0.1,4))}).add;
	SynthDef("impulses",{Out.ar(0,Impulse.ar([0.9,1.19],0,0.3))}).add;
)

//where is the reverb? 
(
	Synth("impulses");
	Synth("reverb");
)

SuperColliderのデフォルトの「お任せ」としていると、このNodesの順序によるトラブルで、思った動作が出来ない場合がある。 サウンドをグループ化するdefaultのNodesでは、以下の2つがユニークである。


// Group(0) is the absolute root of the tree.

r=RootNode.new; 

// Group(1) was added as an additional default to receive
// all created Synths, to avoid cluttering the base of the tree. 

Group.basicNew(s, 1);

以下は、まず440Hzのサインを鳴らしたところでNodesを確認し、その後に880Hzのサインを加えてから、 同様にNodesを確認した例である。


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

Synth("temp__4" : 1000)

NODE TREE Group 0
	1 group
		1000 temp__4
			i_out: 0 fadeTime: 0.019999999552965 gate: 1
	2 volumeAmpControl8
		volumeAmp: 0.17579236626625 volumeLag: 0.10000000149012 volumeGate: 1

{SinOsc.ar(880,0,0.1)}.play

Synth("temp__5" : 1001)

NODE TREE Group 0
	1 group
		1001 temp__5
			i_out: 0 fadeTime: 0.019999999552965 gate: 1
		1000 temp__4
			 i_out: 0 fadeTime: 0.019999999552965 gate: 1
	2 volumeAmpControl8
		volumeAmp: 0.17579236626625 volumeLag: 0.10000000149012 volumeGate: 1

ここに、先の例のreverbとimpulseの順序を逆にして呼び出すと、以下のようになった。 順序付けの動作がよく判るだろう。


(
	Synth("reverb");
	Synth("impulses");
) 

Synth("impulses" : 1003)

NODE TREE Group 0
	1 group
		1003 impulses
		1002 reverb
		1001 temp__5
			 i_out: 0 fadeTime: 0.019999999552965 gate: 1
		1000 temp__4
			i_out: 0 fadeTime: 0.019999999552965 gate: 1
	2 volumeAmpControl8
		volumeAmp: 0.17579236626625 volumeLag: 0.10000000149012 volumeGate: 1

この順序をきちんとコントロールするための記述例が以下である。 この4つの例は、いずれも同じ動作となる。 キーワードの「head」「tail」「after」「before」に注意しよう。


//controlled execution

(
	g=Group.basicNew(s,1);
	Synth.tail(g, "reverb");
	Synth.head(g,"impulses");
) 

// Other ways we might do this:

(
	g=Group.basicNew(s,1);
	Synth.head(g,"impulses");
	Synth.tail(g,"reverb");
)

(
	a=Synth("impulses");
	Synth.after(a,"reverb");
)

(
	a=Synth("reverb");
	Synth.before(a,"impulses");
)

SuperColliderでは、全てのSynthは1000から始まるNodes番号を持つ。 また全てのグループは0から始まるNodes番号を持つ。 このグラフのNodesの最大数は「ServerOptions」で指定でき、 そのdefault数は1024である。 まぁ、十分に大きいだろう(^_^;)。

今日は午後に学科会議、続いて教授会、さらに続いて大学院教授会、さらに続いてデザイン学部情報交換会、 1時間だけアカペラの練習、さらに続いて学食の回転寿司で教職員交歓会・・・とビッシリである。 明日もゼミ学生の再提出リベンジ、さらにゼミ3回生のキックオフミーティングがある。 そして明後日の土曜日には大学院入試があり、他大学からの受験生の面接や専門科目の採点があり、それを終えてセントレアに向かう。 続きは成田からの機内かなぁ。

2011年7月31日(日)

これを書き始めたのは、セントレアから着いて、ロンドンのフライトまでの成田空港のラウンジからである。 続きということで、 Workshop materials for Computer Music (G6002)8. Effects Processing8.3 Global Variables and Environments.html からである。 SuperColliderはオフジェクト指向なので、基本的にはローカル変数だけが許されている・・・と思っていたら、ちゃんとglobalもアリなのであった。 SuperColliderでは、古き時代のプログラマのために(^_^;)、インタプリタが「a」から「z」までの26の広域変数を提供している。 つまり、以下の記述はアリである。

a=9;
ただし、「これはお勧めできない」とある。 当然のことで、複雑にモジュール化したソフトウェアのどこかで、偶然、あるいは間違えて、 この変数は任意に更新できてしまう。 それを参照している方の動作としては、とても困ってしまうのである。 そこで、以下のようにローカル変数として定義するよう推奨している。

(
	var a;
	a=9;		//sets local a, NOT global
)
しかし、ゲームで言えばスコアのデータのように、概念的には広域(全体で共有する)のデータも持ちたいところである。 そこでSuperColliderでは、globalなデータを置くことも出来る仕組みを提供している。 これが「currentEnvironment」であり、以下の例のように、「put」メソッドを使って、あるいはショートカットとして「~」を付けて利用できる。

currentEnvironment;
Environment[  ]

~myvar= 0.9;
0.9

~myvar
0.9

currentEnvironment.put(\myvar, 0.7);
Environment[ (myvar -> 0.7) ]

~myvar
0.7

実は、SuperColliderの「currentEnvironment」とは単なる変数格納領域ではなくて、「IdentityDictionary」という、もっと広い概念のようである。以下の例から、その機能をうかがえる。


a= ();	//make a new IdentityDictionary
(  )

a.put(\hello, 67)
( 'hello': 67 )

a.at(\hello)
67

b= (a:8, fortune:"darmstadt", hello:10);	//shortcut to set one up
( 'a': 8, 'fortune': darmstadt, 'hello': 10 )

これで広域変数のお話はオシマイである。 次は 8. Effects Processing8.4 Effects.html である。ここまでの準備を受けて、ここがこの章の本丸である。

まずは以下のように、サンプルとなるソース音を定義する。 ここでは「impuse」と「continuous」という名前で2つのSynthDefを定義し、 さらに「Group 1」として「a」というグループを定義して、「impuse」を呼び出して鳴らしている。 こんな音がした。


Server.default= s=Server.internal; 
s.scope;

(
	SynthDef(\impulse, 
		{
			Out.ar(0,Pan2.ar(Saw.ar(440,Decay2.ar(
				Impulse.ar(1),0.001,0.1,0.5)),0.0)
			);
		}
	).add;

	SynthDef(\continuous, 
		{
			Out.ar(0,Pan2.ar(WhiteNoise.ar(0.1),0.0));
		}
	).add;  
)

a= Group.basicNew(s,1); //get Group 1

x= Synth.head(a, \impulse);

この道具立てのもとで、最初のサンプルは「Delay」である。あらゆるEffectの基本がDelayである。 以下のように、SynthDefとして定義して、上のサンプルのサウンドソースが起動している状態で、 こんな音 と、 こんな音 の2種類のサウンドが得られた。 SYnthDefで定義されたサウンド「impulse」がオーディオチャンネル「0」に出ているので、 オーディオチャンネル「0」を入力として一定時間だけ遅延させる、というDelayのSynthDefにより「付加」された音が加わっている。 サーバ形式になったSuperColliderでは、このように、サウンド処理をモジュール化することで、全体としての複雑さを回避することで、 トータルとしてとんでもない複雑な信号処理でもシンプルに実現しよう、ということなのが伝わってきた。


(
	SynthDef(\fxexampledelay, 
		{
			arg delaytime=0.1;
			var input, effect; 
			input=In.ar(0,2);
			effect= DelayN.ar(input, 1,delaytime); //max delay of one second
			Out.ar(0,effect); //adds to bus 0 
		}
	).add;  
)

y= Synth.tail(a, \fxexampledelay);
y.free;

y= Synth.tail(a, \fxexampledelay, [\delaytime, 0.4]);
y.free;

とりあえずラウンジでの50分ではこんなところである。 これからギリギリのゲートインであるが、機内で続きは出来るかな(^_^;)。

・・・上から7時間ほど経過して、ここはロシア、シベリアの上空である。 成田からのフライトは予定では12時間半ほどであるが、何故か11時間ちょっとで着くらしく(^_^;)、 機内の表示ではロンドンまであと4時間半ほどである。 周囲はまだまだ映画とかゲームで起きている人もいるが、出発後の食事でいつものようにquarterのワイン4本(計ボトル1本)を美味しくいただき、 爆睡した後に目覚めて、ストレッチ+オレンジジュース+コーヒーで目覚めたところである。 これから最終目的地のHuddersfieldに到着するまでは、翌日からの時差ぼけを避けるために絶対に眠らない覚悟である。 パソコン画面の日本時間は18時過ぎだが現地時間は午前10時。 最終的に到着するのは現地時間で23時ぐらいなので、ここから13時間ほど起き続けることになる。

テキストでは先のDelayのサンプルの後に、


//other UGens to explore:

DelayN, DelayL, DelayC, Delay1, Tap, MultiTap
と書かれている。 これは、メッセージウインドウに「DelayN」などを入れてハイライトさせて「Ctrl+D」で、以下のように出てくるリファレンスであった。

DelayN			simple delay line with no interpolation

DelayN.ar(in, maxdelaytime, delaytime, mul, add)
DelayN.kr(in, maxdelaytime, delaytime, mul, add)

Simple delay line with no interpolation. See also DelayL which uses 
linear interpolation, and DelayC which uses cubic interpolation. 
Cubic interpolation is more computationally expensive than linear, 
but more accurate.

See also BufDelayN.

in - the input signal.
maxdelaytime - the maximum delay time in seconds. used to 
initialize the delay buffer size.
delaytime - delay time in seconds.

(
	// Dust randomly triggers Decay to create an exponential 
	// decay envelope for the WhiteNoise input source

	{
		z = Decay.ar(Dust.ar(1,0.5), 0.3, WhiteNoise.ar);
		DelayN.ar(z, 0.2, 0.2, 1, z);
			// input is mixed with delay via the add input
	}.play
)

(
	// recursive application of delay.

	{
		z = Decay2.ar(Dust.ar(1, 0.5), 0.01, 0.1, Saw.ar(100 + [0, 1]));
		5.do 
			{
				|i|
				z = DelayN.ar(
					RLPF.ar(
						z,
						Rand(100, 3000),
						0.03
					),
					1,
					1 / (2**i),
					1,
					z * 0.5
				)
			};
		z
	}.play
)
このサンプルをコピペして、現在定義しているSynthDefの音源に作用させることが出来れば、 いろいろな応用が可能なので、ちょっとあれこれ試してみて、以下のように並べることでシンプルなエコーが実現できた。 こんな音がした。

(
	SynthDef(\test1, 
		{
			arg delaytime=0.2;
			var input, effect; 
			input=In.ar(0,2);
			effect= DelayN.ar(
				input,
				1,
				delaytime,
				0.7
			);
			Out.ar(0,effect);
			effect= DelayN.ar(
				input,
				1,
				delaytime*2,
				0.5
			);
			Out.ar(0,effect);
			effect= DelayN.ar(
				input,
				1,
				delaytime*3,
				0.3
			);
			Out.ar(0,effect);
		}
	).add;  
)

y= Synth.tail(a, \test1);

y.free;

この「DelayN」というのは、補間をしていないシンプルな(CPU負荷の低い)delayである。 これに対して、以下の「DelayL」は、線形補間をして音質を向上させたdelayである。


DelayL	simple delay line with linear interpolation

DelayL.ar(in, maxdelaytime, delaytime, mul, add)
DelayL.kr(in, maxdelaytime, delaytime, mul, add)

Simple delay line with linear interpolation. See also DelayN which uses 
no interpolation, and DelayC which uses cubic interpolation. Cubic 
interpolation is more computationally expensive than linear, but more accurate.

See also BufDelayL.

in - the input signal.
maxdelaytime - the maximum delay time in seconds. used to 
initialize the delay buffer size.
delaytime - delay time in seconds.

(
	// Dust randomly triggers Decay to create an exponential 
	// decay envelope for the WhiteNoise input source

	{
		z = Decay.ar(Dust.ar(1,0.5), 0.3, WhiteNoise.ar);
		DelayL.ar(z, 0.2, 0.2, 1, z); 
			// input is mixed with delay via the add input
	}.play
)

(
	// recursive application of delay.

	{
		z = Decay2.ar(Dust.ar(1, 0.5), 0.01, 0.1, Saw.ar(100 + [0, 1]));
		5.do 
			{
				|i|
				z = DelayL.ar(
					RLPF.ar(z, Rand(100, 3000), 0.03),
					1,
					1 / (2**i),
					1,
					z * 0.5
				)
			};
		z
	}.play
)
また、以下の「DelayC」は、cubic補間をしたもので、もっともCPUの負荷が高い。

DelayC 	simple delay line with cubic interpolation

DelayC.ar(in, maxdelaytime, delaytime, mul, add)
DelayC.kr(in, maxdelaytime, delaytime, mul, add)

Simple delay line with cubic interpolation. See also DelayN which uses 
no interpolation, and DelayL which uses linear interpolation. Cubic 
interpolation is more computationally expensive than linear, but more accurate.

See also BufDelayC.

in - the input signal.
maxdelaytime - the maximum delay time in seconds. used to initialize 
the delay buffer size.
delaytime - delay time in seconds.

(
	// Dust randomly triggers Decay to create an exponential 
	// decay envelope for the WhiteNoise input source

	{
		z = Decay.ar(Dust.ar(1,0.5), 0.3, WhiteNoise.ar);
		DelayC.ar(z, 0.2, 0.2, 1, z); 
			// input is mixed with delay via the add input
	}.play
)

(
	// recursive application of delay.

	{
		z = Decay2.ar(Dust.ar(1, 0.5), 0.01, 0.1, Saw.ar(100 + [0, 1]));
		5.do
			{
				|i|
				z = DelayC.ar(
					RLPF.ar(z, Rand(100, 3000), 0.03),
					1,
					1 / (2**i),
					1,
					z * 0.5
				)
			};
		z
	}.play
)
また、以下の「Delay1」は、オーディオレートの場合は1フレーム、コントロールレートの場合は1コントロール周期の1サンプルだけdelayさせるものである。使い方はちょっと謎である(^_^;)。

Delay1		single sample delay

Delays the input by 1 sample.
Delay1.ar(in, mul, add)

in - input to be delayed.

Note: for audio-rate signals the delay is 1 audio frame, and for 
control-rate signals the delay is 1 control period.

(
	plot
		{
			var z;
			z = Dust.ar(1000);
			[z, z - Delay1.ar(z)] 
				// [ original, subtract delayed from original ]
		}
)

(
	scope
		{
			var z = LFNoise1.ar(290);
			[z, Delay1.ar(z) > z]
				// [original, 1 if rising, else 0]
		}
)

また、以下の「Tap」は、delayラインからbufferに取り出すものである。 以下の例では、モノラル入力のサウンドがそのまま左チャンネルから出て、delayしたサウンドが右から出ている。 こんな音がした。 サンクトペテルブルグ上空の機内の音であるが、あまり面白い音ではない(^_^;)。


Tap			A single tap into a delayline

Tap.ar(bufnum, numChannels, delaytime)

The Tap UGen allows a single tap at a delay into a buffer. 
bufnum - the index of the buffer to use
numChannels - number of channels of the buffer
delaytime - tap delay; cannot be modulated 

See also: PlayBuf (Tap uses the PlayBuf UGen internally). 

// Create a buffer. 
b=Buffer.alloc(s, s.sampleRate, 1); 
	//enough space for one second of mono audio  

// Write to the Buffer with BufWr, read using two Taps, one for each ear!

(
	SynthDef(\helpTap, 
		{
			|bufnum|
			var source, capture; 
			source= SoundIn.ar(0); 
				//use headphones to avoid feedback
			capture= BufWr.ar(
				source, 
				bufnum, 
				Phasor.ar(0,1, 0, BufFrames.ir(bufnum),1)
			);
			Out.ar(0, Tap.ar(bufnum, 1, [0.1,0.9])); 
				//multichannel expansion, so one tap each ear  
		}
	).send(s); 
)

x=Synth(\helpTap,[\bufnum, b]);

x.free;

また、以下の「MultiTap」は、その名の通りに、マルチタップのdelayである。 こんな音がした。


MultiTap				multiple tap delay

MultiTap.ar(timesArray, levelsArray, in, mul, add, bufnum)

This is a wrapper  which creates a multiple tap delay line 
using RecordBuf and PlayBuf.

timesArray - a Ref to an Array of delay times in seconds.
levelsArray - a Ref to an Array of amplitudes.
in - the input signal.
bufnum - the number of the buffer to use for the delay. T
his must be at least as long as the longest tap time.

s.boot;
b = Buffer.alloc(s, s.sampleRate);

(
	{
		MultiTap.ar(`[0.1, 0.2, 0.3, 0.4], `[0.1, 0.2, 0.4, 0.8], 
		Decay.ar(Dust.ar(2), 0.1, PinkNoise.ar), bufnum: b)
	}.play
)
ここまで2時間ほど経過して、バッテリが45%になった。 あとロンドンまで3時間弱である。 ロンドン空港では乗り継ぎの時間が相当にあるので、続きが出来るかもしれない。

・・・そして上記から数時間後、ヒースロー空港内のレストランバーである。 既に日本時間は8月1日の午前1時過ぎであるが、まだまだ明るい17時過ぎ、太陽を追いかけたこの日はとても長いのである。 国際線の3番ターミナルからここ国内線の5番ターミナルまでは「電車で数分」(free)であった。 これぞ本場の黒ビール「スタウト」とともに、バッテリを充電しながらゆっくり出来るのはいいが、 残念ながら店内にはWiFiが届かない(^_^;)。 メイルのチェックはHuddersfieldに着いてからとなるが、まぁ日曜日、大したメイルも来ていないだろう。

次のサンプルは、エフェクトとしてのVibrateである。 これは定番テクニックとして、Delay時間をLFOで揺すってやればよい。 以下のサンプルでは、 こんな音がした。


(
	{
		var source; 
		var fx; 
		source= Saw.ar(440,0.1);
		fx= DelayC.ar(
			source,
			0.01,
			SinOsc.ar(Rand(5,10),0,0.0025,0.0075)
		);
		fx
	}.play
)

次のサンプルは「Chorus」である。 これも定番テクニックとして、複数系列のサウンドを微妙にずらしてVibrateをかければよい。 以下のサンプルでは、10系統を重ねていて、 こんな音がした。


(
	{
		var source; 
		var fx; 
		var n=10;
		source= EnvGen.ar(Env([0,1,0],[0.1,0.5]),Impulse.kr(2))*Saw.ar(440,0.5);
		fx= Mix.fill(n, 
			{
				var maxdelaytime= rrand(0.01,0.03);
				var half= maxdelaytime*0.5;
				var quarter= maxdelaytime*0.25; 
				DelayC.ar(
					source, 
					maxdelaytime, 
					LFNoise1.kr(Rand(5,10),0.01,0.02) 
				)
			}
		);
		fx
	}.play
)

次のサンプルは「Reverb」である。 リバーブはエコーに比べて奥がとても深い。 たんなる残響であれば、くし型フィルタやオールパスフィルタを使うのが定番であるが、ホールなどのシミュレーションの場合には、 ブリディレイ(アーリーリフレクション)とか色々と要素がある。 以下の例では、ソースはインパルスであり、 こんな音がした。


(
	SynthDef(\fxexamplereverb, 
		{
			arg delaytime=0.01, decaytime=1;
			var input; 
			var numc,numa,temp;
			input = Impulse.ar(1);
			numc = 4; // number of comb delays
			numa = 6; // number of allpass delays

			// reverb predelay time :
			temp = DelayN.ar(input, 0.048,0.048);

			temp=Mix.fill(numc,
				{
					CombL.ar(temp,0.1,rrand(0.01, 0.1),5)
				}
			);

			// chain of 4 allpass delays on each of two channels (8 total) :
			numa.do(
				{
					temp = AllpassN.ar(
						temp,
						0.051, 
						[rrand(0.01, 0.05),rrand(0.01, 0.05)], 
						1
					) 
				}
			);

			// add original sound to reverb and play it :
			Out.ar(0,(0.2*temp));
		}
	).add;  
)

y= Synth.tail(a, \fxexamplereverb);

y.free;

SuperColliderでは、この面倒なリバーブについては、以下のように色々とレディメイド版を用意している。


//readymade Reverbs in SC3.2 and later

FreeVerb
FreeVerb2
GVerb

//If you build your own reverbs, useful UGens are: 

CombN, CombL, CombC
AllpassN, AllpassL, AllpassC

//and the delay reverbs above for early reflections
一例として「FreeVerb」のリファレンスを引いてみると、以下のようになっている。 最後のサンプルでは、「room」「mix」「dump」のパラメータをセットしてみると、 こんな音がした。

FreeVerb	A reverb

FreeVerb.ar(in, mix, room, damp, mul, add)

coded from experiments with faust.

Valid parameter range from 0 to 1. Values outside this 
range are clipped by the UGen.

in - input signal.
Mix - dry/wet balance. range 0..1
room - room size. rage 0..1
damp - Reverb HF damp. range 0..1

See also [FreeVerb2]

Examples

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

// FreeVerb - 1x1 ugen
(
	z = SynthDef(\src, 
		{
			|mix = 0.25, room = 0.15, damp = 0.5|
			Out.ar(0,
				FreeVerb.ar(
					Decay.ar(Impulse.ar(1), 0.25, LFCub.ar(1200,0,0.1)),
					mix, // mix 0-1
					room, // room 0-1
					damp // damp 0-1 duh
				) ! 2 //fan out...
			);
		}
	).play
)

z.set(\room, 0.7)
z.set(\mix, 0.4)
z.set(\damp, 0.2)

z.free

//it expands as any ugen does
(
	z = SynthDef(\src, 
		{
			|mix = 0.25, room = 0.15, damp = 0.5|
			Out.ar(0,
				FreeVerb.ar(
					Pan2.ar(
						Decay.ar(
							Impulse.ar(1), 
							0.25, 
							LFCub.ar(1200,0,0.1)
						),
						LFNoise1.ar(1).range(-1,1)
					),
					mix,
					room,
					damp
				)
			);
		}
	).play
)

z.set(\room, 0.7)
z.set(\mix, 0.4)
z.set(\damp, 0.2)

z.free
・・・ここまでで、まだ「エフェクト」の半分も行っていない。 半透明の空港ビルの屋根の外はまだまだ明るい「昼間」の18:30過ぎである(サマータイム)。 既に日本時間は午前2時半、機内で5時間弱ほど爆睡したものの、2杯目のビールとともに、相当に眠くなってきた。 フライトにはまだ2時間近くあり、搭乗ゲートはその30分前にならないと判明しない。 とりあえずバッテリも満タンになったので、今日はここまでという事で、あとはWiFiの届く場所で日本のニュースでも読んでみる事にしよう。 Huddersfield大学の宿舎に到着して寝ることが出来るまでにはあと数時間である。 マンチェスターで乗る電車を間違えたら大変だ。(^_^;)

2011年8月1日(月)

長い長い移動日に続いて、前夜のオーブニングコンサートは行けなかったものの、 ここは無事にICMC2011の初日、朝9時からの午前の最初のセッション会場である。 昨夜は無事に、マンチェスター空港からマンチェスター市内のピカデリー駅に行き、 さらにHuddersfield行きに乗り換えて、なんとか宿舎にたどりついた。 タクシーがちょっと違った場所に下ろしたのでスーツケースを400mほど押したが、 夜間窓口へのインカムはちゃんと稼働して部屋に入れた。 予定より遅く、この段階で24時を過ぎていた。

満足に寝ていないのに、日本時間が朝になってしまったので夜中に目覚めたのはいつもの事である。 さて、ICMCは毎日、朝9時からのペーパーセッションに始まり、夜の遅いコンサートが終わるのは日付が変わる頃である。 朝イチから眠いのに、とても全部には付き合えないので、ペース配分を考えながらの参加である。 朝イチの2つのsessionは「sound spatialization」であり、ちょうどSuperColliderでやっているところと かなり関係している。

ただし、NIME2011で経験したように、セッションの内職でSuperColliderを進めると、 肝心の研究発表の内容があまり入ってこない(^_^;)。 NIMEではコンサート公演の作家モードだったのでいいとしても、 このICMCは研究発表をするので、研究者モードとして、色々なネタを仕込みたいので、 現場での内職は控えることにした。 SuperColliderの続きは、どこか他のチャンスを狙うことにしよう。

・・・いまは午後4時過ぎ(日本時間では8月2日に入ったところ)、午後のコンサートが終わって、再び午後のpaper sessionである。 昼休みには、久しぶりに Processing日記 の方をちょっと進めてみたが、Synthesisのこのセッションでは、またまたSuperColliderの現在のトピックとかぶっている。 これは「SuperColliderも進めなさい」というお告げと解釈して、チラッとやってみよう。 トピックはフェーズシフタとフランジャーである。

まずはフェーザー(フェーズシフター)である。 解説では「phasing = play a signal back in combination with a phase shifted copy of itself (using an allpass filter); vary the delaytime under 20 msec」とある。 入力信号の位相をシフトさせてフィードバックをかけるが、ディレイ時間は20msec以下でなければならないのだという。 以下のサンプルでは、ホワイトノイズをソースとして、典型的なフェイザーの音がしている。 こんな音がした。 また、最後の2行を実行させてパラメータを変更させると、 こんな音がした。


Server.default=s=Server.internal;

(
	SynthDef(\continuous, 
		{
			Out.ar(0,Pan2.ar(WhiteNoise.ar(0.1),0.0));
		}
	).add;  
)

a= Group.basicNew(s,1); //get Group 1

(
	SynthDef(\fxexamplephasing, 
		{
			arg freq=0.2;
			var input, effect; 
			input=In.ar(0,2);
			effect= AllpassN.ar(input,0.02,SinOsc.kr(freq,0,0.01,0.01)); 
				//max delay of 20msec
			Out.ar(0,effect);
		}
	).add;  
)

x= Synth.head(a, \continuous);

y= Synth.tail(a, \fxexamplephasing);

y.set(\freq, 0.1)

y.set(\freq, 1)

y.free

次はフランジャーである。 解説では「flanging= play a signal back in combination with a delayed copy of itself; vary the delaytime around 10 msec」 「flanging usually also involves some feedback, acheived here using LocalIn and LocalOut」とある。 フェイザーよりもディレイタイムが10msec付近、という違いがある。 これにより、より高域の変化が知覚されるわけだ。 「LocalIn」「LocalOut」のペアでこれを実現する。 以下のサンプルでは、ホワイトノイズをソースとして、典型的なフランジャーの音がしている。 パラメータを変更させると、 こんな音がした。 ちょうどpaperセッションでも、まさに似た音が鳴り響いていた。(^_^;)


(
	SynthDef(\fxexampleflanging, 
		{
			arg flangefreq=0.1, fdback=0.1;
			var input, effect; 
			input=In.ar(0,2); 
			input= input+ LocalIn.ar(2); //add some feedback
			effect= DelayN.ar(input,0.02,SinOsc.kr(flangefreq,0,0.005,0.005));
			LocalOut.ar(fdback*effect);
			Out.ar(0,effect);
		}
	).add;  
)

x.free

x= Synth.head(a, \continuous);

y= Synth.tail(a, \fxexampleflanging);

y.set(\flangefreq,0.4);

y.set(\fdback, 0.95);

y.free;

残りのトピックは、今度は音量に関するエフェクトで、コンプレッサとかリミッタとかディストーションである。 テキストは1本ずっと続いているが、これを日を変えて分割すると、また新たにSynthDefを呼び出したりするのが大変である。 チラッと見たところでは、scopeを見る程度のものであり、実際に聴覚的には違いが顕著でもないので、ここは軽くパスすることにした。 これで初日のpaperセッションは終わり、これからDemoとPosterのパラレルがあり、その後にレセプションがある。 そして晩のコンサート、さらに深夜のコンサートと続く。 これでこそ、クレイジーなICMCである。(^_^)

2011年8月2日(火)

ICMC2011の2日目である。いまは朝イチのAnalysysのセッション(この時間帯はパラレルではないが、昨夜が遅かったので参加者少数(^_^;))である。そこそこ聞き流せるものなので、日記の続きを書こう。

昨日のところで書けなかったが、Proceedingsとして配られたのが、今年はUSBメモリでなくDVDだったので、 いったん宿舎に戻ってUSBメモリを持ち、受付のコンピュータでDVDからコピーして午前後半のセッションに遅れて 参加して着席したところ、ポンと肩を叩かれた。 そこにいたのはなんと、 ロシア でご一緒した、Jon Appleton氏であった(^_^)。 着席してプログラムで調べてみると、なんと僕が深夜に到着したその晩のコンサート1のトップで、 亡くなったMax Mathews先生を偲んで、という特別招待公演で、Jonがラジオバトンを演奏していたのだった。 大学院入試のお仕事が無ければ初日から参加して絶対に聴いたのに・・・と後悔してももう遅い。 その後、Jonはセッションの最後に退席したので、その日は話をする機会が無かった。

午後のコンサート、ペーバーセッション、デモ/ポスターセッションと参加したところで、ホワイエではレセプションの準備が進んでいたが、 そこで、立っているだけで寝てしまいそうな猛烈な眠気がやって来た。 どうも今回は日本を出てから現地到着→睡眠までの時間が長かったので、時差ぼけがキツい模様である。 そこで、深夜のコンサートどころか、晩のコンサートもパスすることにして、レセプションの冒頭でワインを2杯だけ飲んで宿舎に帰った。 かといってスグには眠れず、また夜中に目覚めたらもう眠れないのだが、とりあえず今日に発表があるのでセーブした。

そして今朝、ポスターを持って朝8時に朝食のためにカフェに来ると、なんとそこにJon Appleton氏がやってきて、 一緒に朝食、久しぶりに話をすることが出来た(^_^)。 なんとJohnはこの朝からストックホルムに飛び、さらにヘルシンキに行き、ラジオバトンで計2つのコンサートをこなしてから ボストンに帰るのだという。「君はパワフルで尊敬する」なんて言ってくれたが、Jonの方がずっっっとパワフルな72歳である。 東北の地震のこと、オスロのテロの背景の人種問題、など色々と話をすることが出来て、本当に良かった(^_^)。 次回はミシガンでの再会を期して、Jonは旅立っていった。

さて、そこでSuperColliderである。 続きは、「9. Algorithmic Composition」の 9.1 Algorithmic Strategies.html からである。 これは相当に奥が深い世界であり、なかなか難航しそうな予感がする(^_^;)。 まずは冒頭で「Strategies for Algorithmic Composition」 ということで、

と問題提起している。 これは音楽美学の重要なテーマということで、参考書として 「Pearce, M., Meredith, D. and Wiggins, G. A. (2002) Motivations and Methodologies for Automation of the Compositional Process. Musicae Scientiae 6(2): 119-147」 を掲げている。勉強したい人はどうぞ。

まず最初のexampleとして、以下のSynthDefの定義があった。 これだけでは鳴らないので、だいぶ下の方にあったアルゴリズム作曲のプログラムを続けて合体させてみると こんな音がした。


(
	SynthDef(\acsound,
		{
			|freq=440,amp=0.1,dur= 0.2,cutoff=2000|
			var sound, filter;
			sound= Saw.ar(freq, amp)
				*EnvGen.kr(
					Env([0,1,1,0],[0.01,0.05,(dur.max(0.07))-0.06]),
					doneAction:2
				);  
			filter= LPF.ar(sound,Line.kr(cutoff,300,dur)); 
			Out.ar(0,filter.dup(2))
		}
	).add;
)

(
	var currentvalue= rrand(60,72); 
	var generateandtest; 
	generateandtest= 
		{
			|previous=60|
			var number=rrand(24,127); 
			var keeplooking; 
			while(
				{
					keeplooking= false; //can only fail
					if (abs(number-previous)>12) 
						{
							keeplooking= true; 
						};
					if (#[-5,-3,4,7,11].includes(number-previous)) 
						{
							keeplooking= true; 
						};
					((number.asString)++(if(keeplooking," rejected","accepted"))).postln;
					keeplooking
				},
				{
					number=rrand(24,127); 
				}
			);
			number
		};  
	{
		20.do{
			currentvalue = generateandtest.(currentvalue);
			Synth(\acsound,[\freq, currentvalue.midicps]); 
			0.25.wait;
		};
	}.fork;
)

とりあえず鳴ったものの、どうも詳細不明である(^_^;)。ここは地道に追いかけていこう。 まずテキストでは、「Probability theory in practice」ということで、確率統計の理論から学ぶことになる。 定性的にはほぼ直感的な確認として、「確率分布」というのは、確率空間で「起き得る」可能性の分布である。 これは、確率密度関数pdf(probability density function)で記述できる。 サイコロの目が好例であるが、整数などの離散的な確率についても、一般に「0.0から1.0」などの分布を持つ実数に適当な間隔で配置される。 確率密度関数の総和(積分)は1となるように標準化する。 確率密度関数が定義されれば、cdf(cumulative distribution function)(累積分布)は、ゼロからそれぞれの事象までが起きる累積確率を計算できる。

ここからしばらくは、音を鳴らすわけでもなく、確率統計的に音楽を生成するための数学的な道具立てなどが並ぶ。 まずは「pdfとcdfを活用するために便利な関数」として、以下の「~normalize」と「~cumulative」「~draw」である。


//Helper functions for investigating pdfs and cdfs

(
	//normalize total sum of an array to 1.0
	~normalize = 
		{
			|array|   array/(array.sum)
		}; //note, not safe if array is empty or otherwise sums to zero

	// (could also use normalizeSum, just showing this explicitly)

	// create array of cumulative values, 
	// assuming input is normalized (sums to 1.0)
	~cumulative = 
		{
			|array| 
			var num = array.size; 
			var accumulate = 0.0; 
			var cumul; 
			cumul = array.collect
				{
					|val, i|  
					var old= accumulate; 
					accumulate = accumulate+val; 
					old
				};
			cumul
		}; 

	// use cumulative distribution to find an output value for an input
	// assumes array is a cumulative distribution 
	// function, and array size is at least 1
	~draw= {
		|input, array|
		var nextindex;
		nextindex= array.indexOfGreaterThan(input); 
			//also see indexInBetween if want linearly interpolated index
			//get nil if input greater than anything in array
		if (nextindex.isNil,
			{
				nextindex= array.size;
			}
		);
		nextindex= nextindex-1; 
			//get index before; we 'went past' and have to 
			//go one back to find the slot our input falls in
			//nextindex should never be less than 0
		nextindex/(array.size); 
			//get position proportional within array length
	}
)
上記の関数をpostウインドウで実行させておいて、 以下は、pdfを可視化するためのplotのサンプルである。

p= ~normalize.value(Array.fill(1000,1.0));

p.plot2(minval:0.0, maxval:2.0/1000)  //pdf

以下は、cdfを可視化するためのplotのサンプルである。


c= ~cumulative.value(p); 

c.plot2 //cdf

以下は、ランダム関数を可視化するためのサンプルで、両方とも同じようなものになる。


Array.fill(1000,
	{ 
		~draw.value(1.0.rand, c)
	}
).sort.plot2 
//choose values; driven by uniform random number, 
//could also just provide increasing uniform numbers 
//from 0.0 to 1.0 (and then wouldn't need the sort) 
//we do this a thousand times to simulate 'rolling 
//the dice' many times; the distribution only really 
//shows itself over many trials (and can do it more 
//times for better approximations)

Array.fill(1000,
	{ 
		1.0.rand 
	}
).sort.plot2 
//create values directly

以下は、線形分布の確率密度関数の例である。


//2. Linear distribution, probability density drops 
//linearly, so more likely to get lower values:

p= ~normalize.value(Array.fill(1000,{|i| 1000-i}));

p.plot2  //pdf

c= ~cumulative.value(p); 

c.plot2 //cdf

Array.fill(1000,{ ~draw.value(1.0.rand, c)}).sort.plot2 
	//choose values 

Array.fill(1000,{ 1.0.linrand }).sort.plot2 
	//create values directly 

以下は、「負の指数関数」の確率密度関数の例である。「10」と「100」の2つのパラメータで試している。 重要なのは、「100」の方ではメモリの制限でクウォンタイズが出来ないために、いちばん最後のplotに失敗している事である。


//3. Negative exponential distribution, probability density drops 
//exponentially, so much more likely to get lower values:

//there is a parameter here for the rate of fall off of the distribution 
~alpha= 10.0; 

p= ~normalize.value(Array.fill(1000,{|i| exp((i.neg/1000)*~alpha) })); 

p.plot2  //pdf

c= ~cumulative.value(p); 

c.plot2 //cdf

Array.fill(1000,{ ~draw.value(1.0.rand, c)}).sort.plot2

~alpha= 100.0; 

p= ~normalize.value(Array.fill(1000,{|i| exp((i.neg/1000)*~alpha) })); 

p.plot2  //pdf

c= ~cumulative.value(p); 

c.plot2 //cdf

Array.fill(1000,{ ~draw.value(1.0.rand, c)}).sort.plot2

次のサンプルは、以下の「標準分布(ガウシアン)」の確率密度関数の例である。


//4. Normal distribution (Gaussian)
//two parameters, mean ('centre') and standard deviation ('spread'); 
//here we take sensible values to plot the distribution easily

~mu= 0.5; 
	//mean

~sigma=  0.17; 
	//standard deviation; most of probability mass within 3 
	//standard deviations, so this makes the Gaussian fit 
	//the 0.0 to 1.0 range easily for our plotting; try 
	//changing these parameters later to see the effect. 

//normalization constant calculated automatically, 
//though there is a mathematical expression for it

p= ~normalize.value(
	Array.fill(1000,
		{
			|i| 
			exp(
				(((i/1000)-~mu)/~sigma).squared.neg
			)
		}
	)
); 
p.plot2  //pdf

c= ~cumulative.value(p); 
c.plot2 //cdf

Array.fill(1000,
	{ 
		~draw.value(1.0.rand, c)
	}
).sort.plot2 
	//choose values 

Array.fill(1000,
	{  
		0.5.gauss(0.17).max(0.0).min(1.0)  
	}
).sort.plot2 
	//create values directly, clamping within +-3 standard deviations

次のサンプルは、好きなように定義する、という「任意の分布」である。


//5. Arbitrary distribution
//let's make up our own function

p= ~normalize.value(
	Array.fill(1000,
		{
			|i|  
			var prop= (
				i/1000.0); 
				if(
					prop<0.2,
					{(0.2-prop)**(0.3)},
					{(prop-0.2)**2}
				) 
		}
	)
); 

p.plot2  //pdf

c= ~cumulative.value(p); 

c.plot2 //cdf

Array.fill(
	1000,
		{ 
			~draw.value(1.0.rand, c)
		}
	).sort.plot2 
//choose values 

具体的に以下の例では、MIDIの48-72の音域を0.0から1.0にノーマライズして、 そこにオリジナルの確率分布で20個のノートを選んでいる。


//example in use; 
//20 notes drawn using the custom distribution 
//(0.0 to 1.0 range output rescaled to MIDI notes 48 to 72)

(
	{
		20.do
			{
				Synth(
					\acsound,
						[
							\freq, 
							48+(24*~draw.value(1.0.rand, c))
						]
					); 
				0.25.wait;
			}
	}.fork;
)
このような、統計確率的にノートを生成するアルゴリズムは色々あるので調べてみて、とのことである。 一例として、「Charles Ames. A catalog of statistical distributions: Techniques for transforming random, determinate and chaotic sequences. Leonardo Music Journal, 1(1):55-70, 1991.」と「Denis Lorrain. A panoply of stochastic "cannons". Computer Music Journal, 41(1):53-81, 1980.」を挙げている。

現実的には、スケールとなる音セットのそれぞれの生成確率を定義する、というのがとても多い。 そのためには、


[0,1,2,3,4,5].wchoose([0.2,0.3,0.1,0.1,0.05,0.25])
というように、オリジナルの生成確率を並べればいい。 以下の3つの例を見ると一目瞭然である。

//examination using our machinery for pdf, cdf, and draws:

p= ~normalize.value([0.2,0.3,0.1,0.1,0.05,0.25]); 
p.plot2  //pdf

c= ~cumulative.value(p); 
c.plot2 //cdf

Array.fill(1000,{ ~draw.value(1.0.rand, c)*6}).sort.plot2 
//choose values; multiply by 6 to get back integer indices 

このような方法で、自由な確率統計分布モデルを構築できる。 また、確率統計分布を、音楽の進展(時間経過やシーンの変化)に対応して変化させるというのは自然な発想である。 これは数学的には、確率分布を記述する関数のパラメータを変更したり、異なる関数の間を補間してやればよい。 分布関数として定義されている確率分布を変更するのは、ある種の「リマップ」という事に他ならない。 音楽におけるこのようなアイデアというのは基本的で、例えばグラニュラシンセシスの「tendency masks」というのも同じである。

だいぶ進めてきたが、ここまで、午前の2つのセッション、さらに街に出て昼食をとって帰ってきてからのカフェまで、 かなり時間がかかった。 まだこのセクションの半分程度だが、この後には「条件付き確率」(→確率推定)という、またまた深い世界に突入するので、 今日はこのあたりにしよう。 いまは午後のpaper sessionであり、この最後に抜け出してposterの準備をしないといけない。 またまた頭の奥底に重い眠さが蘇ってきたが、果たして今夜のコンサートには行けるかなぁ。(^_^;)

2011年8月3日(水)

さて、ICMC2011の3日目である。 前日の発表のあたりの経緯は、夜中に Processing日記 の方に書いたので省略である。 この3日目はとても変則的で、新しい「paper+piece」という形態のセッションが2つあり、またこれまでのICMCでは禁断だった 「paperとconcertのパラレル」もある。 そして、General Meeting、さらにバンケット(なんとかパークにピクニック)もある。 プログラムを眺めて考えた上で、昼のコンサートを2回公演いずれもパスして、それらとパラレルの、 2つの「paper+piece」セッション(東大の安藤さんの発表あり)、 さらに神戸大(現在MITメディアラボに滞在中)の竹田さんの発表のある「教育」のセッションに行くことにした。

さて、SuperColliderの続きは、「9. Algorithmic Composition」の 9.1 Algorithmic Strategies.html の後半、「Use of conditionals」からである。 まさにこのSuperColliderのセクションと似た概念による音楽生成プログラムについての解説が、 内職と同時進行のセッションで安藤さんの発表として聞こえてくるのはとても面白い。(^_^;)

アルゴリズム音楽というのは、コンピュータアルゴリズムのビルディングブロックという概念と対応している。 「条件に対応した(分岐)実行」(プログラムのif文とかswitch文)は、現在の状態に対して、次にどう進むか、というdecision makingを支援する。 ここに絶対的な記述をするのが古典的な作曲であり、(擬似的な)確率的記述を用いれば、そこからアルゴリズム作曲の世界が開ける。

実際に簡単な例を見てみよう。 以下は「絶対的な記述」である。ここでは、ノート=60の音は、常に0.2の音量となる。


//deterministic
(
	var pitch=60, amp=1.0; 
	if(pitch==60,{amp= 0.2});
	amp
)
以下は「確率的な記述」である。ここでは、ノート=60の音は、コインを投げて(ランダム)、その値が0.5すなわち確率1/2で、音量1.0または音量0.2となる。

//probabilistic
(
	var pitch=60, amp=1.0; 
	if(pitch==60 and: 0.5.coin,{amp= 0.2}); 
		//amp gets set to 0.5 on average half the time 
	amp
)
音楽において、以下のようないろいろな異なった状態が相互に関係している。 面白いことに、有限ステートマシンのアイデアは、音楽と類似である。 つまり、「現在の状態を継続する」というのも含めれば、音楽の色々な状態が、直前の状態から現在の状態に推移する、 というバラエティと制約を記述することは、アルゴリズム作曲そのものと言える。

ここで話題は「条件付き確率(Conditional probability)」に進む。 もっとも基本的な定義は、以下の「P (B | A)」 = 「Aが起きた時にBが起きる確率」である。 もしAが観測された場合には、「Aが起きた時にBが起きる確率」である「P (B | A)」は、 「AとBがともに起きる確率」である「P (A and B)」を観測して、それを「P (A)」で割ればよい。


P (B | A) = P (A and B) / P (A)  

	where P (B | A) means the probability of B 
	given that A is known to have happened
ここで脚注として「ベイズの定理」について述べている。 以下のベイズの定理は、ある状態から他の状態を計算するのに有用である。 例えば、状態Aがある現象で、状態Bは「world state」というのもアリである。 ベイズの定理は、ある特定の現象を観測することで、world stateすなわち「世界の状態(真理)」を推定できるところが有用なのである。

Bayes theorem

P(B | A) = P(A | B) * P(B) / P(A)
ただし良く知られているように、以下のような批判もある。 「ベイズの定理は事前確率及び尤度を仮定した下で事後確率を与える、というあくまで相対的なメカニズムを表した定理にすぎない。したがって事後確率の計算結果の信憑性や有用性は、事前分布と尤度の設定にかかっており、慎重を期すことが必要である。これはベイズの定理を含むベイズ統計学が、不確実性を含む問題を人によって異なる確率を用いて定式化することを許容する主観確率 (subjective probability) という立場をとっていることによる。この立場はまだ解析対象となっていない新たな問題へのアプローチを可能にするという利点がある一方で、確率の決め方について客観性に欠けるという批判もある(客観確率)。」(Wikipedia)

これを応用して、以下のように、「現在起きたことから、将来起きることを推定する」ことも出来る。 一般的には、直前の状態だけで将来を推定するのでなく、過去の履歴(n次のオーダー)から将来推定する、という手法となる。 これが次に紹介する「マルコフ連鎖(マルコフ過程)」である。


P (B occurs at time n | A occurs at time n-1) 
	= P (A at time n-1 and B at time n) / P (A occurs at time n-1)
・・・ここで午前の最初の「paper+piece」セッションが終わり、学内にある立派なカテドラルホールに移動した。 午前の後半も、引き続き「paper+piece」セッションである。 マルコフ過程の解説から、合間に続けていこう。 過去の確率的選択の結果として現在の状態がある、というアイデアから、以下のように3つのノートに関して、1次のオーダーのマルコフ過程を定義できる。

[\a, \b, \c].choose //0th order system, uniform selection

[\a, \b, \c].wchoose([0.7,0.2,0.1])   //if previously choose \a

[\a, \b, \c].wchoose([0.0,0.5,0.5])   //if previously choose \b

[\a, \b, \c].wchoose([0.1,0.4,0.5])   //if previously choose \c
この3音の場合ですら、ある状態を決定していくためには3*3 = 9通りの確率が必要である。 2次の場合には3*3*3 = 27通りになり、一般的には ノートの数と過去の次数が増えれば、Nth orderの場合には3**(N+1)となり、多次元の行列が必要になる。

以下の1次のマルコフ・システムの例では、 こんな音がした。


//1st order Markov system example

(
	SynthDef(\acsound,
		{
			|freq=440,amp=0.1,dur= 0.2,cutoff=2000|
			var sound, filter;
			sound= Saw.ar(freq, amp)
				*EnvGen.kr(
					Env([0,1,1,0],[0.01,0.05,(dur.max(0.07))-0.06]),
					doneAction:2
				);  
			filter= LPF.ar(sound,Line.kr(cutoff,300,dur)); 
			Out.ar(0,filter.dup(2))
		}
	).add;
)

(
	var markovmatrix; 
	var currentstate=3.rand; //start in one of three states

	markovmatrix= 
		[
			[0.7,0.2,0.1],
			[0.0,0.5,0.5],
			[0.3,0.4,0.3]
		];
	{
		20.do
			{
				Synth(
					\acsound,
					[
						\freq, 
						[48,60,64].at(currentstate).midicps
					]
				); 
					//which probability distribution to use 
					//depends on what state we're in right now
				currentstate = [0,1,2].wchoose(
					markovmatrix[currentstate]
				); 
				0.25.wait;
			};
	}.fork;
)

この「paper+piece」セッション2では、ミキサーのすぐ前の席に座ったが、 講演した研究者がスグ後ろの席でライブMax/MSP/jitterを操作し、 その近く、頭の後ろあたりではサックス奏者が迫真の演奏をしている。 プロジェクションされるスクリーンの中心では、サックスのサウンドがリアルタイムに同心円状の波紋を生み出し、 わらわらと動き回るマルチエージェントからも電子音がマルチチャンネルで生成された。 アルゴリズム作曲によるライブ作品の素晴らしい実例に圧倒されたが、その背景には、 まさにここの話題の確率統計アルゴリズムが走っているのである。

セッションの演奏がいろいろと面白いために内職は進まず、ここは午後のランチタイムに宿舎に帰っての合間である。 Keynote Speechをパスして(^_^;)、ICMA General Meetingから合流するまでの時間しかない。 次のトピックは「検索(Search)」である。サーチはコンピュータがもっとも得意な処理である。 疲れることを知らないコンピュータは、全ての可能性をしらみつぶしに検索できるし、 効率を上げる検索アルゴリズムも多種、提案されてきた。 音楽の領域に適用する場合、膨大なパラメータの組み合わせに対しての検索は重要である。 「ヒューリスティクス」(人間の感性に基づく経験則)は、このサーチツリーを大幅にカットすることで検索の効率を向上させる。

ここで「Charles Ames differentiates」と出て来た。 これは知らないのでWikipediaで調べてみると、どうも技術史に登場するらしい。 ここの文脈では、検索に関して以下の2つがある、という事らしい。

要するに、前者は力ワザで全数探索を行う、というもので、最適な解を見つけることが出来るが、コンピュータパワーが必要である、ということ。 後者は「制約付き探索」で、ヒューリスティクスなどにより簡易に探索する、というものだろう。 検索技術の初期の戦略は、1955年のヒラーとアイザックソンが提唱した。 とりあえずランダムな数を当てはめて、たまたまヒットしたら「それだ !」という、数打ちゃ当たるカモ・・・というものである(^_^;)。 ただしこの場合も、多くは人間のヒューリスティクスで、初期値なりに当たりを付けることがほとんどである。 より改良された探索の戦略としては、バックトラッキング(逆伝播学習)とダイナミックプログラミング(→遺伝アルゴリズムGA?)があるという。 音楽への適用に関しては、人工知能(AI・・・死語?)の機械学習が有効だという。

そして以下の「生成サンプル」があった。 1カ所のミスタイプのバグは見つけて訂正した。実行しても何故か音は出なかったが、ノート候補をルールに従って取捨選択しているのは判った。


//generate and test

(
	var currentvalue= rrand(60,72); 
	var generateandtest; 
	generateandtest= 
		{
			|previous=60|
			var number=rrand(24,127); 
			var keeplooking; 
			//keep searching until a number passes the tests

			while(
				{
					keeplooking= false; //can only fail
						//note we could replace this test with 
						//just generating number in the 
						//allowable range to start with

					if (abs(number-previous)>12) 
						{
							keeplooking= true; 
						};
							//avoid certain intervals
					if (#[-5,-3,4,7,11].includes(number-previous)) 
						{
							keeplooking= true; 
						};
					(
						(number.asString)++(if(keeplooking," rejected","accepted"))
					).postln;
					keeplooking
				},{
					//no need to do anything here, all done in while test function
					number=rrand(24,127); 
				}
			);
			number
		};  
		{
			20.do
				{
					currentvalue = generateandtest.(currentvalue);
					Synth(\acsound,[\freq, currentvalue.midicps]); 
					0.25.wait;
				};
		}.fork;
)

a Routine

116 rejected
55 rejected
105 rejected
47 rejected
26 rejected
44 rejected
37 rejected
106 rejected
69accepted
83 rejected
72accepted
117 rejected
103 rejected
58 rejected
75accepted
119 rejected
115 rejected
33 rejected
44 rejected
117 rejected
105 rejected
33 rejected
94 rejected
89 rejected
81accepted
62 rejected
102 rejected
102 rejected
34 rejected
98 rejected
116 rejected
27 rejected
40 rejected
106 rejected
100 rejected
101 rejected
48 rejected
74accepted
113 rejected
121 rejected
47 rejected
103 rejected
126 rejected
107 rejected
43 rejected
56 rejected
86accepted
96accepted
45 rejected
58 rejected
43 rejected
42 rejected
96accepted
49 rejected
58 rejected
113 rejected
50 rejected
41 rejected
71 rejected
34 rejected
91 rejected
25 rejected
98accepted
28 rejected
34 rejected
43 rejected
36 rejected
42 rejected
102 rejected
26 rejected
80 rejected
111 rejected
28 rejected
119 rejected
91accepted
90accepted
72 rejected
109 rejected
111 rejected
85 rejected
91accepted
30 rejected
55 rejected
80accepted
79accepted
43 rejected
72accepted
83 rejected
124 rejected
112 rejected
47 rejected
65accepted
73accepted
64accepted
72accepted
56 rejected
106 rejected
125 rejected
88 rejected
123 rejected
62accepted

さて、長かったこのセクションも最後のトピック「Sonification of mathematics」である。 これは、僕が音楽情報科学の講義の中で紹介している「数理造形」、つまり数学や物理に基づく論理的なグラフィクスに美を求めるのと同じで、 数学のアルゴリズムを「可聴化」することで音楽を創造しよう、という、サウンド版の数理造形デザイン手法である。

ここで紹介されているのは、カオスのアルゴリズムとして有名な「ロジスティックマップ」を使ってピッチを生成するというもので、 僕がこれをMaxで作って講義に使ったのは1993年のことであった。 その後、このアルゴリズムをライブComputer Musicの作品に使ったのは、1995年-1997年あたりまでだった。


//logistic map function used to generate pitch values

(
	var currentvalue= 1.0.rand; 
	var logisticmap, r;
	r=3.74;  
	logisticmap= 
		{
			|previous=60|
			((1.0-previous)*previous*r).postln;
		};  
		{
			50.do
				{
					currentvalue = logisticmap.(currentvalue);
						//must convert from the value in the range 
						//0.0 to 1.0 to a musically useful pitch value
						//quartertones here
					Synth(
						\acsound,
							[
								\freq, 
								(
									60+((currentvalue*12).round(0.5))
								).midicps
							]
					); 
					0.125.wait;
				};
		}.fork;
)

a Routine

0.69420669057001
0.79394126740354
0.61185850712633
0.88820390219417
0.37137351140136
0.87312254685113
0.41431557321715
0.90754158948579
0.31382284961935
0.80536437684877
0.58625471409559
0.90717486486812
0.31494027402942
0.80691583786061
0.58270198007899
0.90941983051639
0.30808400490094
0.79724925808601
0.60454436584176
0.89412357863458
0.35405310181837
0.85533614088774
0.46277348890148
0.92981705889915
0.24406228658851
0.69001461683283
0.79996522575812
0.59847802887619
0.89872977107919
0.3403944905102
0.83972754421348
0.50334863192802
0.93495806212415
0.22743495088259
0.65714901955859
0.84263765433763
0.49592189748422
0.93493780035872
0.22750087072351
0.6572833997929
0.84247962623945
0.4963268189847
0.9349495389522
0.22746268063412
0.65720555172769
0.84257119025241
0.49609222373784
0.93494288752472
0.2274843203728
0.65724966429639

・・・駆け足だったが、これでようやくアルゴリズム作曲のセクションが終わった。 ちょうどICMA General Meetingの場では、来年のICMC2012がスロベニアで、さらにICMC2013がオーストラリアで開催(シドニーと反対側のパース)、とのことである。

2011年8月4日(木)

ICMC2011も後半に入って、4日目である。 僕は大学院入試のために、初日の前夜のConcert 1に行けなかったが、 フル参加者でもあと明日まで、そして僕は明日はオーブンキャンパスのために帰途につくので、最後の1日も出れない。 今日が本当にICMC2011の最終日である。

昨日は上記のICMA General Meetingの後で、午後のpaper sessionで神戸大(いまMIT メディアラボに短期レジデンス中)の竹川さんの発表を聞いて、いよいよバンケットに出かけた。 University of Huddersfieldに3台の大型バスが乗り付け、全員で移動したのは、30分ほどの場所にある、ヨークシャー彫刻公演(YSP)であった。 Windowsのdefaultのデスクトップのようななだらかな高原に、多数の羊がのんびりと草を食べていて(そこら中、足の踏み場もないほど多数の、小さくて丸くて黒いウンコ(^_^;))、あちこちに現代彫刻家の大きな作品が点在している、という公園である。 ここで、こちらに来て初めて(最初で最後)のフルコースのご馳走とワインを堪能した。

・・・そして今日の朝である。 SuperColliderは、 Workshop materials for Computer Music (G6002)9. argolithmic.html9.2 Patterns.html からである。 前セクションでは個々のノートを確率統計的に生成するアルゴリズムについて検討したが、音楽においては、 個々のノートは独立ではなくて、いくつかのまとまりを持ち、繰り返しの構造があるので、ここも音楽にとって必須の内容である。 まずはいつものように、以下のように「s」という名前でローカルサーバを起動しておく。


Server.default= s = Server.local; 
s.boot;
音楽において、パターンを使うことにより、単音を合成するよりもはるかに効果的に音楽生成を実現できる。 それは、パターンによって、我々が時間的に「次に何が起きるか」を容易に理解できるからである。 この基本的な考え方を、基礎から確認していこう。 SuperColliderのライブラリには、内部的に豊富な機能を持っているが、 初心者はその内側に隠された詳細をいちいち調べることなく、表面的な使い方も出来る。 このセクションにおいては、大文字の「P」はパターンを表している。

まずスタートとして、以下は単音を繰り返すサンブルである。 こんな音がした。


// run this line

a = Pbind.new.play(quant:1.0);

a.stop; // Or stop it with cmd+period; 

以下は、周波数を指定して単音を繰り返すサンブルである。 こんな音がした。


// now run this line

Pbind(\freq, 440).play(quant:1.0);

そして以下は、音列を定義して、繰り返すサンブルである。 個々の音ごとに音量を個別に指定することが出来る。 この「quant」のパラメータは、ある音から次の音までの遅延時間(音価に相当)を指定できるので、任意のテンポのフレーズを作れる。 こんな音がした。


// run this, go back and run some of the others at the same time

(
	Pbind(
		\dur,0.125,
		\midinote, Pseq([0, 4, 0, 7, 4, 0, 0] + 60,inf), 
		\amp, Prand([0.125, 0.2, 0.25],inf)
	).play(quant:1.0)
)

ここで、上に紹介した「Pbind」というクラスをあらためて解説している。 以下のように、「\freq」では、ピッチを指定できる。 こんな音がした。


(
	Pbind(
		\freq, 770 // try changing me to another number!
	).play;
)

(
	Pbind(
		\freq, 880
	).play;
)

(
	Pbind(
		\freq, 999
	).play;
)

以下のように、「Pseq」というクラスは、音列をシーケンスとして指定できる。 「inf」とする事で、無限にループを繰り返す。 こんな音がした。


(
	Pbind(
		\freq, Pseq([100,200,300,400],inf)
	).play;
)

パターンのクラスには、以下のように色々なタイプがある。ただし、ここでいちいち実験するのは省略する。(^_^;)


	// loops through the sequence of the array, perpetually:
Pseq([0,1,2,3],inf)  

	// next value is a random member of the array, after 5 times stop:
Prand([0,1,2,3],5)

	// next value is a random member of the array 
	// except you can't repeat the previous value:
Pxrand([0,1,2,3],inf)

	// next value is a weighted choice from the first array
	// using the weights given in the second argument. 
	// After returning one value, stop:
Pwrand([0,1,2,3], [0.5,0.3,0.1,0.1], 1)

	// next value is the result of evaluating the 
	// given function, in this case 4.rand:
Pfunc({ 4.rand })
これ以上のパターンについては、以下の「Streams」のヘルプを引け、とあった。 引いてみると、そこには膨大なヘルプのリンクがある。大変な世界のようである(^_^;)。

Patterns/Streams Help

Some tutorial overviews:

[PG_01_Introduction]  -  A Practical Guide to Patterns

[Streams-Patterns-Events1]  -  Streams & Routines 
[Streams-Patterns-Events2]  -  Patterns Introduction
[Streams-Patterns-Events3]  -  ListPatterns
[Streams-Patterns-Events4]  -  Environment & Event
[Streams-Patterns-Events5]  -  Event.default
[Streams-Patterns-Events6]  -  Parallel Patterns
[Streams-Patterns-Events7]  -  Practical Considerations

Specific classes:

ListPatterns
Pseq
Pser
Prand
Pwrand
Pxrand
Pshuf
Place
Ptuple
Pslide
Pfsm
Place
...

FilterPatterns
Pseed
Prewrite
Pswitch
Pswitch1
Pn
Pstutter
Pfin
Psync
Pcollect
Pselect
Preject
PdurStutter
Pconst
Pwrap
PdegreeToKey
Pavaroh

event stream specific filter patterns
Pset
Pfset
Pmul
Padd
Psetp
Pmulp
Paddp
Pfindur

other Patterns
Pwhite
Pbrown
Ppatmod
Plazy
Pbind
Phid
PstepNadd
PstepNfunc

Streams
BinaryOpStream
UnaryOpStream
EventStream
EventStreamPlayer

to be continued...
SuperColliderにおいて、パターンとはStreamsを生成するGeneratorであるという。 既にSchedulingのところで学んだように、以下のように1行ずつ実行してみると、 「.asStream」メソッドを使って、バターンからストリームを生成するのを確認することができる。

a = Pseq([1, 3, 400],1);  //make Pattern, a Pseq
a Pseq

x = a.asStream; //turn this Pattern into a specific Stream
a Routine

x.next; //ask for the next value in the Stream
1

x.next; //and so on ...
3

x.next;
400

x.next;
nil
以下の例では、「Pshuf」によって、ランダムに色々なフレーズを生成できる。

(
	var a, x, y;
	a = Pshuf([1, 2, 3], inf);
	x = a.asStream; // this creates a Routine from the Pattern.
	y = a.asStream;
	x.nextN(10).postln;
	y.nextN(10);
)
以下は、ランダム音列を実際に試した例である。 実行するたびに異なったフレーズとなる。 こんな音がした。


(
	var a =  Pshuf([1, 1, 0, 1, 0], 3);
	Pbind(
		\dur, 0.125,
		\midinote, a * 7 + 60, 
		\amp, a * 0.1
	).play
)

パターンを複雑にしたかったら、以下のようにネスティング(入れ子構造)にすればよい。


Pseq(
	[
		Pseq([100, 200, 300], 2), 
		400, 
		500, 
		600
	], inf
);
以下は、ネスティングしたフレーズのサンプルである。 こんな音がした。 このスピードであれば、このスクリプトからフレーズを「読み取る/聞き取る」ことがなんとか出来るだろう。


(
	Pbind(
		\freq, 
		Pseq(
			[
				Pseq(
					[
						100, 
						200, 
						300
					], 
					2
				), 
				400, 
				500, 
				600
			],
			inf
		)
	).play;
)

以下はもう一つのネスティングのサンプルで、ランダムを使っている。 こんな音がした。


(
	Pbind(
		\freq, 
		Pseq(
			[
				Prand([440, 442, 445, 448]), 
				Pxrand([840, 741, 642], 2)
			], 
			inf
		)
	).play;
)

以下の例では、最初の配列からランダムな値を選び、次の配列から2つの値を選んでいる。


(
	a = Pseq(
		[
			Prand([440, 442, 445, 448]), 
			Pxrand([840, 741, 642], 2)
		],
		inf
	).asStream;
	20.do(
		{
			a.next.postln;
		}
	);
)

442
741
642
440
840
741
448
840
642
440
642
840
445
741
840
442
642
741
440
840

20
SuperColliderの「Pbind」には、見えない色々なパラメータがあり、指定しなければdefaultになっている。 「Pbind」のクラスでは、パラメータは必ずペアになっていて、パラメータのタイプを示す「\property」に続いてその「associated Pattern (or value, or stream)」が並ぶようにする。 以下の例では、そのいくつかを明示的に指定している。 こんな音がした。


(
	var clock;
	clock = TempoClock(1.5); 
		// tempoclock at 90 bpm
	Pbind(
		\freq, Pseq([440, 660, 990, 880, 770], inf),     
			// frequency in hertz
		\dur, Pseq([1.0, 0.5],inf),               
			// duration of event in beats
		\legato, 0.5,               
			// proportion of inter onset time to play
		\pan, Pseq([0.5, -0.5],inf),
		\instrument, \default
	).play(clock);
)

・・・ここで、午前の後半のセッションが終わった。まだセクションの残りが半分ほどある。

そしてここから、前日に呼びかけられていたランチミーティングに参加した。 呼びかけてきたのは、韓国のKeimyung UnviersityのSeongah Shin女史である。 彼女はICMAのboard memberで、アジアオセアニア地域の担当である。たぶん小坂さんの後任なのだろう。 ここに参加した日本人は、神戸大の竹川さん、東大の安藤さん、昭和音大の由雄さんと僕の4人であるが、 他の参加者としては、2007年の 台湾ツアー でお世話になったYu-Chung Tseng教授、 sMuleを生み出した、この世界の若きスター、スタンフォードCCRMAのGe Wang氏、などなど、こんな顔ぶれ であった。

僕は参加していなかったが(後期スグという時期では参加できない(^_^;))、去年あたりに、アジアの電子音響音楽の作曲家のコミュニティが北京でコンサートをしたとかで、そのあたりから、Seongah Shin女史が音頭とりをして、アジア・オセアニア地域でのComputer Music関係者を連携して、再来年のオーストラリアでのICMCを支援し、さらに将来的には「その次のアジアオセアニアでのICMC」に向けて行きたい・・・という感じである。 なかなかに盛り上がり、今後の交流を誓った、有意義なランチミーティングとなった(^_^)。

そして、このあたりで、つまり国際会議に参加して4日目頃になり、ようやく英語がスムースに聞けてきて、またそこそこスムースに英語で話せるようになってきたのを実感したが、もう明日には帰国である。 これはいつもの事だが、1週間の海外出張というのは、調子が出て来たところで帰る(また忘れる)、というのが、本当に勿体ない(^_^;)。

ここから午後のコンサートの最初の2曲を聞けずに遅刻して(^_^;)入場、その後、午後のpeper sessionに2つ参加したところで、 またまた襲ってきた猛烈な眠気に負けて、夕方以降の予定をパスして宿舎に戻り、爆睡した。 「時差ぼけ」に関する今回の教訓は、「日本出発から24時間を越える旅程だと、時差ぼけがキツい」という経験則の確認である。 かつてサラリーマン時代に、日本からロスへ、さらにダラスへ、そしてニューオーリンズへ、と計25時間ほどかけて移動した時にも、 その後経験したことのない深い時差ぼけを体験していたのを思い出した。 欧米へのフライトは12時間-13時間程度であるが、そこで現地に着いてとりあえず寝た場合には、これほどひどくはならない。 途中の乗り継ぎなどの合計で24時間レベルになると、いくら機内で寝ても駄目のようである。

2011年8月5日(金)

さて、日付としてはICMC2011の最終日である。 ただしこれを書いているのは深夜というか早朝というか午前3時過ぎであり、実際に最終日の朝イチのpaper sessionが始まるまでには、 まだ数時間ある。 昨夜は21時頃に早々に寝たが、やはり身体の奥底の日本時間によって、午前2時半(日本は午前10時半)には目覚めた。 平日の昼間ということで、届いたいろいろなメイルに対応しつつ、ここからはもう寝ないで出発していくつもりである。 その方が、成田へのフライトでしっかり眠れると思う。

いろいろ届いたメイルの中で、M1の伊熊さんは、「新しいインスタ作品の企画書」という課題pdfを提出してきたが、これが素晴らしいものだった。 M1でこれが作れたら凄いし、場合によっては発展させて修了制作を目指すようなサウンドインスタレーションになるかもしれない。 また、卒業生の山村知世さんからは、前々回前回、に続いて、また開催するという個展の案内が届いた。 また焼津に出かけてみよう(^_^)。

学科教員MLからの情報では、明日からのオープンキャンパスの準備が、例年より早まって、13時からと予告された。 教員パネルの印刷の和田先生はたぶんもう2日以上、寝ていないだろう。 他の学科教員も、いろいろな仕事も回ってきて、なかなか大変な模様である。 2回生・3回生を中心に、さらに1回生にも動員がかかってることだろう。本当に申し訳ないが、ここは35虎の学生に全てを任せよう。

さて、出発まで4時間弱、ここはSuperColliderの続きを区切りまで進めてみたい。 Workshop materials for Computer Music (G6002)9. argolithmic.html9.2 Patterns.html の後半からである。以下まで進めていた。


(
	var clock;
	clock = TempoClock(1.5); 
		// tempoclock at 90 bpm
	Pbind(
		\freq, Pseq([440, 660, 990, 880, 770], inf),     
			// frequency in hertz
		\dur, Pseq([1.0, 0.5],inf),               
			// duration of event in beats
		\legato, 0.5,               
			// proportion of inter onset time to play
		\pan, Pseq([0.5, -0.5],inf),
		\instrument, \default
	).play(clock);
)
このプロバティに関しては、テキストでは「[Meta_Event:makeParentEvents]」のヘルプを引け、とあり、 このように 長大な定義が、defaultとして用意されている。 これはもちろん、ユーザでも独自に定義して拡張できる。 以下の例では、新たなプロパティを持つSynthDefを定義して、それをPbindから与えている。 ただし、原因は不明であるが、録音してみると大きなクリック音みたいなのが重なってしまい(^_^;)、 こんな音がした。


// run me first

(
	SynthDef(\alicepavelinstr, 
		{
			arg out=0, 
			alice=440, 
			pavel=0.5, 
			pan=0.0, 
			gate=1;
			var z;
			z = Resonz.ar(
				Pulse.ar(alice, pavel),
				XLine.kr(5000,1000),
				0.1,
				5
			) * Linen.kr(
				gate, 0.01, 0.1, 0.3, 2
			);
			Out.ar(out, Pan2.ar(z, pan));
		}
	).add
)

(
	var clock;
	clock = TempoClock(1.5); 
		// tempoclock at 90 bpm
	Pbind(
		\alice, Pseq(440*[1,2,3],inf),   // freq
		\pavel, Pseq([0.1,0.5, 0.8],inf), // pulse width
		\dur, Pseq([0.5,0.25,0.25],inf), // duration of event in beats
		\legato, 0.5, // proportion of inter onset time to play
		\instrument, \alicepavelinstr // your own synthesiser
	).play(clock);
)

当然、全てのプロバティは互いに独立している。 ただし、それを敢えて関係付けることも出来る。 以下の例では、ランダム生成される音量パラメータに周波数が影響される、というものである。 こんな音がした。


// cobinding of properties

(
	Pbind(
		[\freq, \amp], 
		Pseq(
			[
				[440,0.4],
				[330,0.1],
				Pfuncn(
					{
						[550.rand, 0.8.rand]
					}, 
					1
				)
			], 
			inf
		)
	).play
)

以下の例では、過去に生成したプロパティ値をチェックして、新しい値をセットする前に同じ値であった場合に知らせている。 こんな音がした。


// checking already decided properties of the Event
// that will be performed before setting a new value

(

	Pbind(
		\freq, Pseq(
			[ 
				440, 
				330, 
				Pfuncn(
					{ 550.rand + 40 }, 
					1
				)
			], 
			inf
		),
		\amp, Pfunc(
			{ 
				arg event; 
				event.postln; 
				if(event.freq > 350, 
					{
						"here".postln; 
						rrand(0.1,0.5);
					}, 
				0.05); 
			}
		)
	).play
)

ここまでの道具立てを受けて、いよいよ音楽として面白いパターンを作り出していこう。 以下の例では単音フレーズでなくポリフォニーにして、さらに音楽的に改良している。 こんな音がした。


// two simultaneous voices using Ppar

(
	var melodypat, basspat;
	melodypat = Pbind( 
		[\midinote, \dur], 
		Pseq([
			[60, 0.75],[64, 0.5],[66, 0.5],[69, 0.25],
			[67,0.75],[64,0.5],[60,0.5],[57,0.25]
		],inf)
	);
	basspat = Pbind( 
		\midinote, Pseq([48, 42], inf),
		\dur, 1
	);
	Ppar([ melodypat, basspat ]).play(TempoClock(1)); 
)

カオス力学の世界には、既に登場したロジスティック写像の他に、以下の式に従うヘノン(エノン)Henonの写像というものもある。


x(n+1)=1-a*x(n)*x(n)+y(n)
y(n+1)=b*x(n)
ちょうど広島大学のサイトに こんなページ があり、Flashでのシミュレーションとして こんなページ の中に、 これこれ があった。 以下のサンプルは、そんなエノン写像のアトラクタ(周期解)として、 「-1.5こんな音がした。


(
	p = Prout(
		{ 
			var x0, y0, x, y;
			x0 = 0; y0 = 0;
			loop(
				{
					x = y0 + 1 - (1.4 * x0 * x0); 
					y = 0.3 * x0; 
					x0=x; y0=y;
					[x, (y*14).asInteger].yield;
				}
			); 
		}
	);

		// \degree is the degree of the scale provided 
		// in \scale- 
		// this is where the dorian tuning comes from
	b = Pbind(
		\scale, [0,2,4,5,7,9,11], 
		\dur,0.125,
		[\pan, \degree], p
	);

		// the order of arguments in Pbindf has been 
		// switched since SC2- this may change back 
		//again- be careful! 
	Ptpar(
		[ 
			0.0, 
			Pbindf(b, \octave, 4,\stretch, 3.0), 
			4.0, 
			b
		]
	).play(TempoClock(1));
)

ここまで「Pbind」を使ってパターンを生成するサンプルを紹介してきたが、アルゴリズム作曲において、 「Pbind」を使わずにパターンをもっと簡単に生成する事も出来る。 そして「スケジューリングの所で学んだparallel処理で実現したサンプル」というのがあったが、 いつものようにどこかにバグがありエラーが出たので(^_^;)、ここでは省略する。

SuperColliderでは、SynthDefにパターンをaddするためのライブラリとして、「SynthDesc」というのを用意しているという。 見てみると このように これまた凄い世界になっている。 ただしここでは詳細は省略である(^_^;)。 以下のように、順序としては、まずSynthDefにパターンを定義してストアした後に、Pbindでそれを読み出す、ということになる。 こんな音がした。


// run me first-
(
	SynthDef(\nickinstr, 
		{ 
			arg out=0, freq=440, amp=0.1, pan=0, gate=1;
			var z;
			z = LPF.ar(
				Mix.ar(LFSaw.ar(freq*[0.99,1,1.01],0.0,amp)),
				XLine.kr(5000, 1000, 1.5)
			) 
			* EnvGen.kr(
				Env.new([0,1,0],[0.01,0.01],\lin,1), 
				gate, 
				doneAction:2
			); 
			Out.ar(out, Pan2.ar(z, pan));
		}
	).store; 
)

(
	Pbind(
		\dur, 1.25,
		\midinote, Pseq([0,5,0,7,4,0,0]+60,inf), 
		\amp, Prand([0.125,0.2,0.25],inf), 
		\instrument, Pseq([\nickinstr,\default],inf),
		\pan, Prand([-1,0,1],inf)
	).play
)

個別のプロパティの付加については「Pfx」のリファレンスを引け、というので見てみると、 以下のようなサンプルがあった。 同じパターンを、今度は繰り返すたびに異なったエフェクトを加えて切り替えて、全体としては2周回ったところで止まる。 こんな音がした。


// run me first-
(
	SynthDef(\echo, 
		{ 
			arg out=0, maxdtime=0.2, dtime=0.2, decay=2, gate=1;
			var env, in;
			env = Linen.kr(gate, 0.05, 1, 0.1, 2);
			in = In.ar(out, 2);
			XOut.ar(
				out, 
				env, 
				CombL.ar(in * env, maxdtime, dtime, decay, 1, in)
			);
		}, 
		[\ir, \ir, 0.1, 0.1, 0]
	).store;

	SynthDef(\distort, 
		{ 
			arg out=0, pregain=40, amp=0.2, gate=1;
			var env;
			env = Linen.kr(gate, 0.05, 1, 0.1, 2);
			XOut.ar(
				out, 
				env, 
				(In.ar(out, 2) * pregain).distort * amp
			);
		}, 
		[\ir, 0.1, 0.1, 0]
	).store;

	SynthDef(\wah, 
		{ 
			arg out=0, gate=1;
			var env, in;
			env = Linen.kr(gate, 0.05, 1, 0.4, 2);
			in = In.ar(out, 2);
			XOut.ar(
				out, 
				env, 
				RLPF.ar(
					in, 
					LinExp.kr(
						LFNoise1.kr(0.3), 
						-1, 
						1, 
						200, 
						8000
					), 
					0.1
				).softclip * 0.8
			);
		}, 
		[\ir, 0]
	).store;
)

(
	var p, q, r, o;
	p = Pbind(\degree, Prand((0..7),12), \dur, 0.3, \legato, 0.2);
	q = Pfx(p, \echo, \dtime, 0.2, \decay, 3);
	r = Pfx(q, \distort, \pregain, 20, \amp, 0.25);
	o = Pfx(r, \wah);
	Pseq([p, q, r, o], 2).play;
)	

・・・これでようやく、このセクションが終わった。 実はこの次に、 Patterns - Pair Programming Exercises という「課題」があるのだが、まぁこれは学生がそれぞれトライして欲しい。

SUACではもうオーブンキャンバス準備の修羅場が始まったのか、伊熊さんに続いてM1の田畑さんの企画書が届いたぐらいで、 メイルもめっきり静かになってきた。 出発まであと1時間半ほどなので、シャワーを浴びて荷物を整理して、ぼちぼち準備していくことにしよう。 本来なら、ヨーロッパからの帰国日は太陽から遠ざかるので、行きに比べて1日がとても短いのだが、 今回はなんせ午前2時から起きているので、なかなかに長い。 空港での待ち時間がそこそこあるので、この続きがまだ書けるかもしれない。 (→ここまでをWebに上げた後に、35虎の明歩ちゃんから「インスタ設置完了(^_^)」のメイルが届き、安心して出発した)

・・・ということで、ここはマンチェスターからロンドン・ヒースローに向かうBMI機内である。 往路のヒースロー→マンチェスターはBA(British Airline)で、機内ではビールもワインもタダだったのに、 さんざんヒースロー空港内のパブで飲んでいたのでオレンジジュースにした。 今日の復路では、まず空港に着いたところのカフェで朝食のホットドッグにビール、 さらにチェックインしてからのカフェで、これぞ本場のギネスをまたまた堪能したが、 さらに機内でワインを頼んでみら、有料だった(^_^;)。 ・・・と書いていたらもうヒースローに到着した。1時間のフライトは短い。

そしてここからは、ヒースロー空港内のバーである。 時間がタップリあるので、コンセントのある席で、昼食にオムレツとデキャンタの赤ワインを頼んだ。 どうも英国では、高い店でないと生野菜のサラダをゲットできないみたいである。 いつものようにさんざん飲みつつ撮り、これは後日のフォトレポートに載るわけだが、 往路に乗り換えのマンチェスターピカデリー駅のコンビニで買った1本、 そしてFiddersfield大学内の売店(生協)で買った1本、計2本の赤ワインのボトルは写真ナシである。 5泊の部屋飲みでワイン2本というのは、僕にすればかなり大人しいペースであった。 さて、SuperColliderの方は、なんと遂に「OSCとの連携」のセクションだが、 空港内のWiFi環境ではなんか安定しないので、これは帰国後にとっておく事にした。 そこで、ワインをちびちびやりながら、あれこれICMC2011とかのメモをだらだら書こう、というところである。

海外で英語に浸かりきる時間の中で、これまでもっとも印象に残っているのは、 NIME03の大会委員長が、毎朝のセッション冒頭でのアナウンスで言った、 「皆さんに良いニュースと悪いニュースがあります」という言葉である。 これを聞きつければ、誰でも注目して集中して、無駄話を止める。 「Good News is ・・・」と前置きして、例えば「○○○の場所では、タダでコーヒーを提供します」程度の ショボい情報でも、ちゃんとウケるものである。 そして「Bad News is ・・・」と続ければ、誰でも注目する、 ここで、プログラムの内容に変更があって混乱させて申し訳ないが・・・というようなお詫びも、誰でも許してしまう。 これはすぐに毎日のセッションで、皆んながマネを始めて大流行した。 例えば研究発表の中でアドリブで、「私のこの研究の報告においては、Good PointとBad Pointがあります」 などと、皆んなパロッたのである(^_^)。 最終日の大会委員長のclosing messageで、 「最後に、悪いニュースがあります。NIME03が終わってしまい、皆さんとお別れしなければなりません」 と言った時の最高潮の盛り上がりと拍手喝采は素晴らしかった。 翌年のNIME04では、大会委員長として、僕も挨拶にこのフレーズを使って、予想通り、ウケた(^_^;)。

さて、そこで、今年のICMC2011の「Good Point」と「Bad Point」を、思いつくまま書いていこう。 まずは「Good Points」である。 最初に感謝したいのは、天気である。 事前にYahoo.comのweatherで調べてみたら、最低気温が7度とか8度、最高気温が13度とか15度、 ということだったが。天気のいい時には英国も好天なのだった。 たまにバラッと雨が降ることもあったが、基本的に最低気温が15度、最高気温が22-24度、 そして空気は快適に乾燥していて、とてもとても快適だった。 日本に帰った時の蒸し暑さが、今から想像するだけで嫌になる(^_^;)。 また、いま帰国時の日本の天気予報をYAHOOで見ているが、なんと台風9号が沖縄を直撃、 さらに台風10号も発達しているのに、どうやら成田とセントレアは無事である。 僕が不在の今週中、静岡県では震度5の地震もあったし、本当に僕の行くところ、天変地異が起こらない。 これ以上のラッキーは無いのである。

それから「宿」である。 イギリスの大学は、いまは9月からのセメスター(前期)の前の休みである。 そこで、ICMC参加者の大部分は、実行委員会が手配した、University of Huddersfieldの隣にある学生宿舎、 Asplay Hallに宿泊した。 ここはまず、当然ながら、安い。なのに個室にシャワーとトイレがあり、キッチンだけが共同である。 全てのドアにはセキュリティシステムが稼働して、施設に入るにも、自分の建物に入るにも、 受け取ったタグが無いと入れない。 そしてフロアごとの鍵、数部屋単位のブロックごとの鍵、そして自室の鍵、と完備している。 過去には「共同シャワー」「共同トイレ」というドミトリーも体験しているが、 それに比べて、まったく気楽で助かった。 そして全ての部屋には有線LANがある。 無料だとメイル程度の低速だが、クレジットカード手続きをすると、 「松」「竹」「梅」のような3段階のスピードのブロードバンドも選べる。 僕は「竹」を選んだが、自分のサーバへのSFTPも通したので、毎日、自分のWebを改訂できた。 (なのに、今現在、このサーバがたまたま停止していて、自分で自分のWebを見れない(^_^;))

そして「Bad Point」である。これは「田舎」に尽きる(^_^;)。 有名な観光地での国際会議であれば、ちょっとサボッて観光に・・・という悪魔の囁きがある。 過去の例では、ザッと挙げても、 北京ベルリンパリ/カッセル/ハンブルクシカゴ/ソルトレークシティ/リノニューヨークモントリオールシンガポールロサンゼルスパリ/アムステルダム/リンツバンクーバーリンツ/バルセロナパリ台北ニューヨークコペンハーゲン/ロンドン/リンツプロビデンスロンドンパリ/ウイーン/リンツシドニーリンツエカテリンブルクオスロ、 などの海外出張では、合間にチラッと抜け出しては、これぞという見所を探訪・堪能してきた。 ところが今回のICMC2011では、Huddersfieldという知らない場所で、要するにド田舎だった。 マンチェスターまで1時間、さらにマンチェスターからリバプールまで1時間程度であるが、 ビートルズにそれほどの思い入れのない僕は、結局、リバプール観光に脱出することもなく、 大人しく、宿舎と会場を往復した。(^_^;)

もう一つの「Bad Point」は、既に書いてきたように、現地までの到達時間が長かったこともあり、 時差ぼけを吸収しないうちに去ることになった、という点である。 結局、晩のコンサートも深夜のコンサートも全滅だった。 これは、「研究発表」と「コンサート」という2つの柱があるNIMEやICMCでは常のことであるが、 僕は時には研究者として研究発表を行い、時には作曲家として作品公演を行う。 このモードの違いで、

  • 「研究者モード」で発表する時には、paper sessionに注力して、コンサートはパスする事が多い
  • 「作曲家モード」で発表する時には、concert sessionに全力投球(自分/他人の作品)して、paperを手抜きする
というように、参加形態はまったく変わるのである。 オスロのNIME2011はまさに後者、そして今回のICMC2011は前者の典型となってしまった。 両方をフルに頑張ると、最後には体力が尽きるか会期が終わるか・・・という厳しい競争となる。

・・・ここらでぼちぼち、ようやく日本へのフライトが近づいてきた。さらば英国。 ついでに、「Part5」をここまでとして、次は「Part6」として再開することにしよう。


SuperCollider日記(6)