25. Stream

25. Stream

Stream : value들의 a lazy sequence로서 다음 값은 어떤 message를 보냄으로 받을 수 있기 때문에 lazy란 이름이 붙는다. sequence가 시작되고 reset되고 멈추는것도 message를 통해서 이루어진다. stream의 길이나 규모는 한정적이거나 무한할 수 있고, 한정적일때, stream이 끝나면 nil이 된다.

Stream and its subclasses
math등을 stream에 실행하거나 filtering 할수 있는 class가 있다.
일반적으로 FuncStream이 쓰이는데, 이는 next, reset등의 메세지에 의해서 실행된다. 
(
var a;
a = FuncStream.new({ #[1, 2, 3, 4].choose });
5.do({ a.next.postln; }); // print 5 values from the stream
)
또 다른 예제로는 Routine이 있다. Routine은 중간에서 어떤 값을 보내서나 어떤 시점에서 resume될 수 있다. ‘yield’메세지가 Routine으로 부터 값을 부르게 하고, 값은 Routine 이 다시 시작될때, 멈추었던 시점에서 다시 시작한다. 
(
var a;
a = Routine.new({ 
  3.do({ arg i; i.yield; }) 
 });
4.do({ a.next.postln; }); // print 4 values from stream
)
(
var a;
a = Routine.new({ 
  3.do({ arg i; 
  (i+1).do({ arg j; j.yield; }) 
  }) 
 });
8.do({ a.next.postln; }); // print 8 values from stream
)
Math on Streams
(
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; });
)
(
var a, b;
// a is a stream that counts from 0 to 9
a = Routine.new({ 
  10.do({ arg i; i.yield; }) 
 });
b = a + 100; // add a constant value to stream a
12.do({ b.next.postln; });
)
(
var a, b, c;
// a is a stream that counts from 0 to 9
a = Routine.new({ 
  10.do({ arg i; i.yield; }) 
 });
// b is a stream that counts from 100 to 280 by 20
b = Routine.new({ 
  forBy (100,280,20, { arg i; i.yield }) 
 });
c = a + b; // add streams a and b
12.do({ c.next.postln; });
)
Filtering on streams
.collect, .select, .reject의 메세지를 사용하여 값을 추출해낼 수 있다. 
(
var a, b;
// a is a stream that counts from 0 to 9
a = Routine.new({ 
  10.do({ arg i; i.yield; }) 
 });
// b is a stream that adds 100 to even values
b = a.collect({ arg item; if (item.even, { item + 100 },{ item }); });
6.do({ b.next.postln; });
)

(
var a, b;
// a is a stream that counts from 0 to 9
a = Routine.new({ 
  10.do({ arg i; i.yield; }) 
 });
// b is a stream that only returns the odd values from stream a
b = a.select({ arg item; item.odd; });
6.do({ b.next.postln; });
)

(
var a, b;
// a is a stream that counts from 0 to 9
a = Routine.new({ 
  10.do({ arg i; i.yield; }) 
 });
// b is a stream that only returns the non-odd values from stream a
b = a.reject({ arg item; item.odd; });
6.do({ b.next.postln; });
)


reference : SC3 메뉴얼

23. Tdef

Tdef task reference definition

superclass: TaskProxy


Tdef는 reference를 task(time pattern)으로 유지시켜서 play되는 동안에 값이 전환될 수 있도록 해줍니다.
이는 old stream이 끝났을때 플래이 되고, 새로운 stream이 결정되고 beat의 단위로 schedule을 주게 됩니다.

Tdef(key)는 instance를 보내며, Tdef(key,pat)은 pattern을 결정하여 instance를 보냅니다. 이는 Pdef와 Ndef와 유사하며 TaskProxy와 비슷합니다. 

-instance message 
 *new(key, obj)
  task 를 key에 따라 global dictionary 에 저장합니다. 
  만약 이미 다른 Tdef가 있다면 이것은 새로운것으로 대체가 됩니다.
  obj 는 function 또는 시간값을 가진 pattern 입니다.
   
 *new(key)
  key에 있는 task로 접근합니다. (만약 아무것도 없으면 default task 가 생성됩니다.)
   
 *default
  a default source, 만약 아무것도 주어지지 았으면 default는 loop 으로 1.0 beat wait time을 무시합니다.
  
 *removeAll 모든 proxies를 제거
 *clear 모든 proxies 를 clear
 
 *all
  모든 저장된 Tdef들을 불러냅니다.
 
 *all_(envir)
  global environment를 set합니다. 
   
 quant_(beats)
  quantisation time 을 beat 의 단위를 가진 정확한 scheduling을 위해 쓰게 해줍니다. 
  [timingOffset, quant] 와 같이 pair로 사용가능합니다.
   
 clear 그 source 를 nil로 set
 
 *defaultQuant_(beats)
  default quantisation 을 new instances (default: 1.0)로 set
  [timingOffset, quant] 와 같이 pair로 사용가능합니다.

 condition_(func)
  새로운것이 삽입되었을때에 pattern이 적용되는곳에 어떤 condition 을 제공 합니다.
  stream value 와 count 가 그 function으로 적용됩니다.
  methods count_(n) 은 count를 n 까지 사용하게 해주며 그때 pattern을 켜줍니다.
 
 reset  
  pattern을 곧바로 켜줍니다. (stuck conditions 이 이로 인해 파괴될 수 있습니다.)


 envir_(event)
  default environment 가 proxy를 위해 제공됩니다. 
  주어진 후에 이것은 routine function의 environment 로 적용됩니다. 
  이것이 처음으로 주어졌을때는 routine pattern 이 재생성됩니다.
 
 set(key, val, key2, val2, …)
  arguments 를 environment에 set해줍니다.
  environment에 아무것도 없으면 주어진 값이 생성되고 routine pattern은 재생성 됩니다.

 endless
  a proxy를 지속적으로 play해주는 Proutine을 보냅니다. 
  이는 nil값을 default값으로 전환시켜 줍니다.(1 s. wait time)
  이는streams을 만들어 새로운 pattern이 삽입될때 까지 기다립니다.


a) stream reference로 사용할 때.
 
 
 embed 는 embedInStream의 축소형
 
 embedInStream(inval)
  stream처럼 stream위에 놓게 합니다.
 
 

 
b) Task로 사용될 때

 play(clock, doReset, quant)
  Pdef를 시작하여 player를 만듭니다.
  여러개의 instances를 재생하기 원하면 .playOnce(clock, doReset, quant)를 사용합니다. 여기서 quant는 [quant, phase]로 pair로 사용가능합니다.
   
 stop
  task를 멈춥니다.
 
 player 
  현재의 task (만약 Tdef이 단순히 다른 stream에서 사용되면 이것은 nil)

 pause / resume / reset / mute / unmute
  등이 적용될 수 있습니다. 
   
 isPlaying 
  Tdef가 현재 사용되고 있으면 true를 보냅니다.
  Tdef 가 play되고 있으면 그것의 stream 을 끝내고, 새로운 function이 적용될 때에 바로 task가 play될 수 있도록 schedule 해줍니다.




a) task player로의 사용.


Tdef(x).play; // 빈 Tdef를 만들고 play합니다. (즉 환경을 먼저 만듭니다.)

Tdef(x, { loop({ “ggggggggggggggggg9999ggg999ggg999gg”.scramble.postln; 0.5.wait; }) });// loop가 0.5초마다 적용되거 ” ” 내에 있는것을 scramble 시켜서 보내는 새로운 instance가 생성되었습니다.


Tdef(x, { loop({ “———////———————-“.scramble.postln; 0.25.wait; }) });//이제 새로운것으로 대체 되었습니다. 
Tdef(x, { loop({ thisThread.seconds.postln; 1.wait; }) });//이제 Thread가 초단위로 나가게 됩니다.
Tdef(x, { loop({ thisThread.seconds.postln; 1.01.wait; }) });//schedule시간을 변경해줍니다.

TempoClock.default.tempo = 2;//비트단위로 변경

Tdef(x, { “the end”.postln });//시간값이 없으니 instance만 보내고 멉춥니다.
Tdef(x, { “one more”.postln });//하나 더 보내봅니다.
Tdef(x, { loop({ “some more”.scramble.postln; 0.25.wait; }) });//다시 시간값을 주니 변합니다. 

TempoClock.default.tempo = 1;//시간단위 변경

Tdef(x).stop; //멈춥니다.
Tdef(x).play;//다시 실행

Tdef(x).clear;//instance를 지워줍니다.




// 소리의 예제

(
//간단한 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;
)
Tdef(x).play;//Tdef를 x라는 이름으로 했음을 살펴보아주세요~
Server
(
Tdef(x, { 
 loop({
  s.sendMsg(“/s_new”, “pdef_grainlet”, -1,0,0, freq, rrand(600, 640));
  0.1.wait;
 })
})
)//이제 x에 instance를 만들어 줍니다. 서버에 메세지를 보내야겠지요. Synthdef는 늘 서버에 저장되니까요. 그다음 새로운 instance를 만들어 주고(“/s_new”), synth이름을 불러주고 (“pdef_grainlet”), nextNodeID는 nil이니 -1, group 0(RootNode), targetID 0, 그다음 argument값을 random값으로 바꾸어 주었습니다. 

(
Tdef(x, { 
 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;
 })
})
)
//위에는 새로운 패턴을 적용시킨 예제입니다. Pseries로만든 stream을 freq에 적용

(
Tdef(x, { 
 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;
 })
})
)
//다음은 Plazy를 사용하여 stream들을 만들어 주었습니다. 
// metronome
Tdef(y, { loop({ s.sendMsg(“/s_new”, “pdef_grainlet”, -1,0,0, freq, 1500); 1.wait; }) }).play;
//여기에 새로운 y란 이름으로 메트로놈을 만들어 주었어요.

// play ending stream once
(
Tdef(x, { 
 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.postln);
  dt.wait;
 })
})
)

//다시 x 를 바꾸어 줍니다. dt값은 schedule을 컨트롤 하는데 쓰입니다. 자 여기서 이전의 Plazy는 freq를 컨트롤 하는데 사용되었는데, 여기서는 instance를 여러개 만들어 주기 위해서 사용되었습니다. loop가 없어서 한번의 stream이 사용되고 마쳐집니다. 여기서 item은 Plazy로 생성된 item을 받는것이죠. 값을 확인하고 싶다면 freq, item.postln해보세요.


Tdef(x).stop;
Tdef.removeAll;



b) embed 와 fork: 다른 Tasks / Routines내에 쓰이는 Tdef


(
Tdef(a, { “one”.postln; 1.wait; “two”.postln });
Tdef(c, { var z; z = Synth(default); 0.5.wait; z.release });
r = Task({
 “counting…”.postln;
 2.wait;
 Tdef(a).embed;
 1.wait;
 Tdef(c).embed;
 “done.”.postln;
});
)
//Tdef a는 one출력후 1초 기다리고, 다시 two를 출력하라이고
//Tdef c는 z란 default synth를 만든다음에, 0.5초 기다리고 출력하라고 되어있습니다.
//r은 task를 만들어서 위의 값에 stream을 주었는데요, counting한다음, 2초 기다리고, a를 보내고, 1초 기다리고, c를 보낸다음 done을 보냅니다.
//여기서 중요한 점은 Task안에 Tdef를 불러낼때에 play가 아닌 embed를 사용한다는 것.
r.play; // r 을 출력합니다.

Tdef(c, { var z; z = Synth(default, [freq, 300]); 1.5.wait; z.release }); // change the def
//c를 변경했네요.
r.reset;//reset하고
r.play;//다시 play

// 물론 Tdefs은 다른 Tdef들과도 사용가능합니다.
(
Tdef(b, {
 “counting…”.postln;
 2.wait;
 Tdef(a).embed;
 1.wait;
 Tdef(c).embed;
 “done.”.postln;
});
)
Tdef(b).fork;//fork를 사용한 출력입니다. 

// 만약에 stream하나를 분리해서 thread에 넣고 싶다면 asStream을 사용하면 됩니다.
// .playOnce 도 사용가능합니다. 
(
Routine({
 Tdef(c).fork;
 0.1.wait;
 Tdef(c).fork;
 0.1.wait;
 Tdef(a).fork;

}).play;
)

//Routine과 사용될때 fork가 사용됬다는 접을 기억하세요. 




Tdef as a time pattern

Pdefn을 time값으로 사용하지 않고 Tdef를 사용하는것도 유용합니다.
source를 변경할때에 stream의 값들을 clock으로 synchronized 되도록 유지시켜 줍니다.


(
// 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;//저장하고
)




Tdef(z, Pseq([1, 1, 1, 0.5, 0.5], inf));
하나의 Tdef를 먼저 정의해줍니다. 

(
 Pset(instrument, pdef_grainlet, //instrument로 위에 주어진 synthdef를 사용하고
  Ppar([
  Pbind(
  dur, Tdef(z),//duration값을 Tdef에서 불러줍니다.
  note, Pseq([1, 3, 2, 1, 0], inf),//note값을 Pseq으로 새롭게 정의
  x, Pfunc { TempoClock.default.elapsedBeats.postln } // onset times 을 출력해줍니다. 
  ),
  Pbind(
  dur, 4, //이것은 reference beat으로 사용될 값입니다.
  sustain, 0.1,
  note, 8
  )
  ])
 ).play(quant:1);
)


Tdef(z, Prand([1, 1, 0.23, 0.5, 0.5], inf)); //time pattern을 변경하기가 이리 쉽습니다.
Tdef(z, Pseq([1, 1, 1, 1], inf)); // pattern을 synch하도록
Tdef(z, Pseq([1, 1, 1, 0.5, 0.5], inf)); // 다른 순서로 정렬될 가능성이 있습니다. (

  // 이를 피하기 위해서 quant의 값을 설정해 주면 됩니다.

reference : SC3 메뉴얼





24. Polymorphism

Polymorphism

Polymorphism은 여러가지 다른 방법으로 하나의 메세지에 응답하는 다른 클라스들의 능력을 이야기한다. 어떤 메세지는 일반적으로 그 내면의 의미를 담고있고, 그것이 각각 클래스의 책임으로서 그 의미에 적절한 방법으로 대응하기 위한것이다.

예를들어 value라는 메세지는 “이 오브젝트의 적절한 값을 주세요’라는 의미를 가진다.

그 value의 방법은 다음의 클라스에 정해져 있다.
Function : this.value(args)
Object : this.value()
Ref : this.value

그렇다면 클래스가 정의된 방법을 보자.
 여기 클라스 Object에 있는 value방법이 있다.
value { ^this }
위는 단순히 그것을 return 하라는 명령이다. 모든 클래스들은 클라스 Object에 의해 전해져 내려오므로, 이 뜻을 클라스가 value를 무시하지 않는 한 그 오브젝트는 그 자신을 return시킴으로서 value 에 응답한다. 

5.postln; // posts itself
5.value.postln; // value returns itself
‘a symbol’.postln;
‘a symbol’.value.postln;
[1,2,3].value.postln;
//etc..

클라스 Function에서 value방법은 기본으로 들어간다.
 value { arg … args; 
  _FunctionValue 
  // evaluate a function with args
  ^this.primitiveFailed 
 }

_FunctionValue 는 C code기본으로 이것이 무엇을 하는지는 이것만 보아서는 알수 없다. 그러나 그것이 하는일은 그 function을 처리하고 그 결과를 보내는 일이다.
{ 5.squared }.postln; // posts Instance of Function
{ 5.squared }.value.postln; // posts 25

Ref 클라스는 오브젝트의 간접적인 reference을 만드는 방법을 제공한다. 이는 reference 에 의해 value 를 통과하도록 쓰일 수 있다. Ref Obkect들은 하나의 instance variable ‘value’를 갖는다. 그 ‘value’방법은 그 instance variable의 ‘value’의 값을 보낸다. 다음 예제를 보자.
Ref : AbstractFunction 
{
 var <>value;
 *new { arg thing; ^super.new.value_(thing) }
 set { arg thing; value = thing }
 get { ^value }
 dereference { ^value }
 asRef { ^this }

 //behave like a stream
 next { ^value }
 embedInStream { arg inval;
  ^this.value.embedInStream(inval)
 }

 printOn { arg stream;
  stream << “`(” << value << “)”;
 }
 storeOn { arg stream;
  stream << “`(” <<< value << “)”;
 }
}
****^의 의미는 return. class에서 ^가 없으면 instance를 return한다. 
****this의 의미는 instance(서두에 instance method가 있는 경우)또는 class (서두에 classMethod가 있는 경우)
Ref.new(123).postln;//ref를 내보낸다.
Ref.new(123).value.postln;//ref의 값을 내보낸다.

Ref는 또한 dereference라는 메세지를 만든다. dereference는 value method가 하는 일과 다르지 않은 instance variable로 value를 보낸다. 그럼 이것은 왜 필요할까? 그것은 어떻게 다른 class들이 dereference에 응답하는가에 의해 설명된다. dereference메세지는 “당신을 포함하고 있는 어떤 Ref든 없애라” 라는 메세지를 의미한다. class Object에서 dereference는 그 object자체를 보낸다. 이는 value 메세지와 같은 양상이다. 차이점은 다른 class들이 이 방법을 취하하지 않는다는 것이다. 따라서 어떤 Function의 dereference는 여전히 Function이다.

Object : this.dereference()
Ref : this.dereference()

5.value.postln;  
{ 5.squared }.value.postln;  
Ref.new(123).value.postln;

5.dereference.postln;  
{ 5.squared }.dereference.postln;//여기를 주목. 위에서는 25를 출력했다.  
Ref.new(123).dereference.postln;//dereference의 값을 내보낸다. ref를 보내는것이 아니다.


또 다른 예제로는 play가 있다. 
{ PinkNoise.ar(0.1) }.play; // Function

( // AppClock
var w, r;
w = GUI.window.new(“trem”, Rect(512, 256, 360, 130));
w.front;
r = Routine({ arg appClockTime;
  [“AppClock has been playing for secs:”,appClockTime].postln;
  60.do({ arg i;
  0.05.yield;
  w.bounds = w.bounds.moveBy(10.rand2, 10.rand2);
  w.alpha = cos(i*0.1pi)*0.5+0.5;
  });
  1.yield;
  w.close;
});
AppClock.play(r);
)

( // SynthDef
x = SynthDef(“Help-SynthDef”, 
 { arg out=0;
  Out.ar(out, PinkNoise.ar(0.1))
 }).play;
)

Pbind(degree, Pseq([0, 1, 2, 3],inf)).play; // Pattern

reference: SC3메뉴얼