27. Environment

Environments 와 Events는 patterns을 위한 상징적인 event framework를 세우기 위해 사용되며, pattern을 사용한 composition을 컨트롤 하도록 한다. 

-Environment

Environment 는 IdentityDictionary으로 symbol들을 value들로 맵핑한다. 
언제나 하나의 current Environment가 존재하여 class Object의 currentEnvironment class variable에 저장된다.

Symbol 과 value는 다음과 같이 하나의 짝으로 current Environment에 저장된다.:

currentEnvironment.put(myvariable, 999);

다시 symbol에 저장된 값을 보고 싶다면 다음과 같이 출력한다.

currentEnvironment.at(myvariable).postln;

맨위에 있는 것을 다음과 같이 간단하게 쓸 수 있다. :

~myvariable = 888;

또한 값을 확인하려면:

~myvariable.postln;


-Environment 만들기

Environment는 class method “make”로 만들어 진다. 그리고 그 안에 값을 저장한다. 
make가 하는일은 일시적으로 current Environment에 새로운 값을 할당하는 일이다. 

(
var a;
a = Environment.make({
 ~a = 100;
 ~b = 200;
 ~c = 300;
});
a.postln;
)

-Environment 사용하기 

instance method “use” 는 currentEnvironment의 값을 사용하도록 한다.

(
var a;
a = Environment.make({
 ~a = 10;
 ~b = 200;
 ~c = 3000;
});
a.use({
 ~a + ~b + ~c
}).postln;
)

There is also a use class method for when you want to make and use the result from an Environment directly.

(
var a;
a = Environment.use({
 ~a = 10;
 ~b = 200;
 ~c = 3000;
 ~a + ~b + ~c
}).postln;
)

-current Environment로부터 arguments와 함께 Function불러내기

아래 예제를 보면, x, y, z를 가진 function f 를 생성하여, 그 값을 .valueEnvir(또는 valueArrayEnvir)를 통해서 지정해준다. 여기서 지정되지 않은 값은 .use에 있는 값으로 사용된다. 

(
var f;

//function
f = { arg x, y, z; [x, y, z].postln; };

Environment.use({
 ~x = 7;
 ~y = 8;
 ~z = 9;
 
 f.valueEnvir(1, 2, 3); // 모든 값이 설정됨
 f.valueEnvir(1, 2); // z 는 current Environment lookup에서
 f.valueEnvir(1); // 
 f.valueEnvir; // 
 f.valueEnvir(z: 1); // 
});
)

다음은 SynthDefs와 함께 Environment를 사용할 수 있는 예제를 보자.
아래와 같이 3개의 function들이 각각 freq, amp 와 pan 의 argument들이 서로 다른 순서로 설정되었으나, 그것과 상관없이 valueEnvir 가 environment에서 각각의 값을 불러낼 수 있다.

(
var a, b, c, n;

n = 40;
a = { arg freq, amp, pan;
 Pan2.ar(SinOsc.ar(freq), pan, amp);
};
b = { arg amp, pan, freq;
 Pan2.ar(RLPF.ar(Saw.ar(freq), freq * 6, 0.1), pan, amp);
};
c = { arg pan, freq, amp;
 Pan2.ar(Resonz.ar(GrayNoise.ar, freq * 2, 0.1), pan, amp * 2);
};

Task({
 n.do({ arg i;
  SynthDef(“Help-SPE4-EnvirDef-” ++ i.asString, {//n개 만큼의 SynthDef가 만들어 지려면 그 각각 이름에 다른 번호가 매겨지도록 사용된 것이 ++i.asString이다. 
  var out;
  Environment.use({
  //environment에 값을 지정해준다.
  ~freq = exprand(80, 600);
  ~amp = 0.1 + 0.3.rand;
  ~pan = 1.0.rand2;
   
  // 다음은 random으로 instrument function을 그 value와 함께 고르도록 한다.
  out = [a,b,c].choose.valueEnvir;
  });
  out = CombC.ar(out, 0.2, 0.2, 3, 1, out);
  out = out * EnvGen.kr( 
  Env.sine, doneAction: 2, timeScale: 1.0 + 6.0.rand, levelScale: 0.3 
  );
  Out.ar( 0, out );
  }).send(s);//서버에 보내는것을 잊지 말아야.
  0.02.wait;//task에 사용될 시간 지정
 });
 loop({
  Synth( “Help-SPE4-EnvirDef-” ++ n.rand.asString .postln);//random으로 synthdef를 뽑도록 해준다. 
  (0.5 + 2.0.rand).wait;//여기서 바로 synth를 불러준다. 
 });
}).play;
)


26. Patterns

Patterns이란 여러개의 stream들을 하나로 묶어주어 출력하고 콘트롤 하게 해주는 방법이다.
따라서 stream에서 쓰이는 방법들과 많이 일치한다. 

앞에 Stream에서 사용됬던 FuncStream처럼 쓰이는것이 Pfunc으로 거의 같은 역할을 한다. 앞에 FuncStream에서는 stream자체가 정의되고 next라는 메세지를 통해서 값을 출력해주었지만 pattern에서는 Pfunc이 값을 만들고, 그것을 Stream으로 만들어 주는 asStream이라는 메세지가 필요하다. 

(
var a, b;
a = Pfunc.new({ #[1, 2, 3, 4].choose });
b = a.asStream; // make a stream from the pattern
5.do({ b.next.postln; }); // print 5 values from the stream

)


또한 앞에서 Routine이 Stream을 만드는데 쓰였지만, 여기서는 Prout가 사용되어 이것이 Routine 을 보낸다.
(
var a, b, c;
a = Prout.new({ 
  3.do({ arg i; 3.rand.yield; }) 
 });
// make two streams from the pattern
b = a.asStream;
c = a.asStream;
4.do({ b.next.postln; }); // print 4 values from first stream
4.do({ c.next.postln; }); // print 4 values from second stream
)
물론 위의 예제만 보면 더욱 복잡해 보인다. 그러나 일정한 값을 지정해준 후, 그것으로 여러개의 다른 stream을 만들어 낼 수 있다는 점을 상기하면 pattern을 복잡한 data를 생성하고, 여러개의 parameter를 동시에 control할 수 있는 방법 중 하나가 된다.


Math on Patterns
Stream의 경우와 거의 비슷하다. Pattern Object가 Stream(Routine이 했던 일)의 자리를 대신한다는 것만 제외하고..
(
var a, b, c;
// a is a pattern whose stream counts from 0 to 9
a = Pseries.new(0,1,10);
b = a.squared; // pattern b is a square of the pattern a
c = b.asStream;
12.do({ c.next.postln; });
)
//Stream에선 아래와 같았다. 
(
var a, b;
// a is a stream that counts from 0 to 9
a = Routine.new({ 
  10.do({ arg i; i.yield; }) 
 });
b = a.squared; // stream b is a square of the stream a
12.do({ b.next.postln; });
)
비교해보면, variables는 3개가 들어가지만, Pattern의 경우 그 구조가 훨신 간단하다.


Filtering on Patterns
Stream과 같이 .collect, .select, .reject의 메세지를 사용하여 값을 추출해낼 수 있다. 
(
var a, b, c;
// a is a pattern whose stream counts from 0 to 9
a = Pseries.new(0,1,10);
// b is a pattern whose stream adds 100 to even values
b = a.collect({ arg item; if (item.even, { item + 100 },{ item }); });
c = b.asStream;
6.do({ c.next.postln; });
)
(
var a, b, c;
// a is a pattern whose stream counts from 0 to 9
a = Pseries.new(0,1,10);
// b is a pattern whose stream only returns the odd values
b = a.select({ arg item; item.odd; });
c = b.asStream;
6.do({ c.next.postln; });
)
(
var a, b, c;
// a is a pattern whose stream counts from 0 to 9
a = Pseries.new(0,1,10);
// b is a pattern whose stream that only returns the non-odd values
b = a.reject({ arg item; item.odd; });
c = b.asStream;
6.do({ c.next.postln; });
)

Reference : SC3 메뉴얼


24. TaskProxy

TaskProxy event stream reference

superclass: PatternProxy

TaskProxy는 task(time pattern)의 reference를 저장하는 공간이며, play되는 동안 다른 값으로 대체가 가능합니다.
하나의 stream이 끝나고 새로운 schedule의 beat이 변경될때(즉 count의 변화등) 유용하게 사용됩니다.



 *new(source)
  새로운 function을 가진 instance를 만듭니다. 그 소스는 routine function(Tdef참고) 또는 시간값을 가진 pattern이어야 합니다.
 
 
 *default
  아무것도 주어지지 않으면 default는 아무것도 하지않고 0.1의 wait time을 가진 loop입니다.
   
 source_(obj)
  source를 설정합니다. 만약 quantization이 주어지면 이 변화를 다음 beat으로 schedule합니다.
  object 는 routine function으로 안전한 방법으로 계산하는데 그렇기 때문에 만약 proxy가 멈출 경우 그것을 알려줍니다. object 는 time 값을 가진 pattern 이 주어져야 합니다.

 clear source를 nil로 설정.
 
 quant_(beats)
  quantization value로 설정. [quant, offset]의 pair로 설정 가능.
 
 quant
  quantization value를 받음
 
 *defaultQuant_(beats)
  default quantization value는 1.0, [quant, offset]의 pair로 설정 가능.
 
 condition_(func)
  새로운 pattern이 켜지면 새로운 condition을 제공합니다. stream 값과 count 가 그function에 적용됩니다. .
  methods count_(n)은 단순하게 n까지 count하고 그다음 pattern을 켜줍니다.
 
 reset  
  pattern을 reset시켜줍니다.
   
 envir_(event)
  proxy를 위해 default environment 를 제공합니다.
  주어진 다음 routine function을 위한 environment 로 사용됩니다. 
  맨 처음 주어졌을때는 routine pattern이 재 생성 됩니다.
 
 set(key, val, key2, val2, …)
  environment에 arguments를 설정.
  아무값이 주어지지 않으면 이것이 생성되고 routine pattern이 재 생성 됩니다.
 
 endless
  Proutine를 출력하고 그것이 이 proxy를 지속적으로 실행하도록 합니다. 이는 nil을 default(1 s. wait time)로 전환하기 때문입니다. 
  이것은 또한 stream을 만들어 새로운 pattern이 삽입될때까지 기다리도록 해 줍니다.
  

   

a) stream reference로 사용하기 
 
 source_ 
  routine function / pattern을 설정 (내부적으로 *new(key, obj)에 의해 만들어짐)
   
 
 embedInStream(inval)
  어느stream과 마찬가지로 그 자신을 stream상에 놓이도록 합니다. 
 
 
 
b) EventStreamPlayer로 사용하기

 play(clock, protoEvent, quant)
  TaskProxy를 만들어서 player를 생성합니다.
  만약 여러개의 instances를 play하고 싶다면 .playOnce(clock, protoEvent, quant)를 사용합니다.
  quant 는 array로 설정가능합니다.-> [quant, phase]
   
 stop
  stop
 
 player 
  현재 player (만약 TaskProxy가 단순히 다른 stream들에서 사용되면 이 값은 nil)

 pause / resume / reset / mute / unmute
  perform player method 
   
 isPlaying 
  만약 TaskProxy 가 진행되고 있으면 true값을 줍니다. 
  만약 TaskProxy 는 진행되고 그 stream이 끝나면 새로운 stream이 주어졌을때 바로 시작될 수 있도록 stream을 schedule해줍니다.
  
 




a) TaskProxy를 player로 사용하기


// 자. 앞에서 배운 Tdef와 비교하여 보세요.
x = TaskProxy.new;//이것은 없었던것이죠? x 에 새로운 TaskProxy설정해줍니다.
x.play; 


x.source = { loop { “ggggggggggggggggg9999ggg999ggg999gg”.scramble.postln; 0.5.wait; } };


x.source = { loop { “———////———————-“.scramble.postln; 0.25.wait; } };
x.source = { loop { thisThread.seconds.postln; 1.wait; } };
x.source = { loop { thisThread.seconds.postln; 1.01.wait; } };

TempoClock.default.tempo = 2;

x.source = { “the end”.postln };
x.source = { “one more”.postln };
x.source = { loop { “some more”.scramble.postln; 0.25.wait; } };

TempoClock.default.tempo = 1;

x.stop;
x.play;
x.stop;

//Tdef와 별반 다를것이 없죠. Tdef는 사실 이 TaskProxy의 편리한 사용을 위함이기 때문입니다. 


// 소리의 예제

(
// 앞장에서 쓰인 synthdef입니다. 
s.boot;
SynthDef(“pdef_grainlet”, 
 { arg out=0, freq=440, sustain=0.05;
  var env;
  env = EnvGen.kr(Env.perc(0.01, sustain, 0.3), doneAction:2);
  Out.ar(out, SinOsc.ar(freq, 0, env))
 }).store;
)
x.play;//앞에서 쓰인 x값이 나오죠

(
x.source = { 
 loop {
  s.sendMsg(“/s_new”, “pdef_grainlet”, -1,0,0, freq, rrand(600, 640));
  0.1.wait;
 }
}
)
//자 여기서 x 가 변경되어 쓰입니다. freq가 변하고 있어요.source가 쓰였습니다. 

(
x.source = { 
 var x;
 x = Pseries(300, 20, 100).loop.asStream;
 loop {
  s.sendMsg(“/s_new”, “pdef_grainlet”, -1,0,0, freq, x.next);
  0.05.wait;
 }
}
)
//앞장에서 Tdef(x, — 가 사용되었는데 여기서 다른점은 맨 앞에 x를 TaskProxy로 설정한 이후로 그저 x.source만 사용됩니다. 

(
x.source = { 
 var x;
 x = Plazy { Pseries(300 + 300.rand, 10 + 30.rand, 10 + 30.rand) }.loop.asStream;
 loop {
  s.sendMsg(“/s_new”, “pdef_grainlet”, -1,0,0, freq, x.next);
  0.05.wait;
 }
}
)

// metronome
(
y = TaskProxy { 
 loop { s.sendMsg(“/s_new”, “pdef_grainlet”, -1,0,0, freq, 1500); 1.wait; } 
};
y.play;
)

// play ending stream once
(
x.source = { 
 var x, dt;
 dt = [0.1, 0.125, 0.05].choose;
 x = Plazy { Pseries(1300 + 300.rand, 110 + 130.rand, 16) }.asStream;
 x.do { arg item;
  s.sendMsg(“/s_new”, “pdef_grainlet”, -1,0,0, freq, item);
  dt.wait;
 }
}
)

… and so on …

x.stop;
y.stop;
//앞장과 같아요~



b)다른 task나 Routine으로 TaskProxy를 embedding하기 

//이것은 앞에 것을 하나로 묶어준 것입니다.
(
#a, c = { TaskProxy.new } ! 2;//a 와 c 를 TaskProxy로 동시에 넣어줍니다. 그러기 위해 쓰인 #를 보세요. 이것을 지운후 실행해보면 에러가 나옵니다. !2도 잊지말아야겠죠.
a.source = { “one”.postln; 1.wait; “two”.postln };
c.source = { var z; z = Synth(default); 0.5.wait; z.release };
r = Task {
 “counting…”.postln;
 2.wait;
 a.embedInStream;
 1.wait;
 c.embedInStream;
 “done.”.postln;
};
)

r.play; // play 

c.source = { var z; z = Synth(default, [freq, 300]); 1.5.wait; z.release }; // change the def

r.reset;
r.play;

//TaskProxies는 또한 다른 Tdef에서도 사용할수 있습니다. 
(
b = TaskProxy.new;
b.source = {
 “counting…”.postln;
 2.wait;
 a.embedInStream;//다시 a를 부르고
 1.wait;
 c.embedInStream;//c 도 부르고
 “done.”.postln;
};
)
b.playOnce;

// 만약에 stream을 다른 분리된 곳으로 넣고 싶다면 asStream을 사용합니다.
(
Routine {
 c.asStream.play;
 0.1.wait;
 c.asStream.play;
 0.1.wait;
 a.asStream.play;

}.play;
)

Reference : SC3 메뉴얼