/* class 8 */
/* Celia says - "Again! Again! Again!" */

/* Patterns */
/*
Patterns and Streams are one of the most convenient, and most annoying aspects of SuperCollider (in my opinion). For storing and generating data, they can be amazing. They can also be used to make sound and pieces. However, there is quite a bit about them that can at times seem magical... and as a result there is sometimes a real lack of control in them... but first, the good stuff.
Patterns are usually named with a P, then describe their usage. Patterns themselves don't have much function, but will output values when they are placed into a Stream. Routines are similar to Streams (it will reply to 'next', 'reset' and 'embedInStream'. But while Routine is able to evaluate large and complex function and operate on data, Patterns and Streams do one thing really well... iterate through data
*/
a = Pseries(0, 1, 10); // arithmetic series, start at 0, inc by 1, 10 times
b = a.asStream;
12.do({b.next.postln});
b.reset;
b.next;
// take an array, and step through it repeat times
a = Pseq([60, 65, 63, 67].midicps, 2);
b = a.asStream;
10.do({b.next.postln});
// pattern classes can also take other patterns as arguments
a = Pseq([60, 65, Pseries(0, 1, 5), 67, 68], 1);
b = a.asStream;
10.do({b.next.postln});
// here are some very common Patterns, and some possible ways to use
// them for data iteration
// Pser takes an array of values, a number of values to return from the series and an offset:
a = Pser([1, 2, 3, 4, 5], 10, 0); // a series, 10 values wanted, start with the 0th element
a.asStream.all; // here is the stream!
a = Pser([1, 2, 3, 4, 5], 10, 2); // start with the 2ndth element
a.asStream.all;
// supports math!
a = Pser([1, 2, 3, 4, 5], 10, 2) * 100;
a.asStream.all;
// supports arrays!
a = Pser([1, 2, 3, 4, 5], 10, 2) * [10, 100];
a.asStream.all;
a.asStream.next
// Pseq takes a repeats value... says how many times to repeat ALL the items in the list
a = Pseq([60, 62, 64, 65, 67, 69, 71, 72], 2); // 2 instances of this list
a.asStream.all; // careful... don't use .all on an inf Stream!
// Pshuf shuffles the list first... then repeats that ENTIRE list repeat tims
a = Pshuf([1, 2, 3, 4, 5], 3);
a.asStream.all;
// Random patterns can be seeded, but...
a = Pseed(123, Pshuf([1, 2, 3, 4, 5], 3)); // ...for some reason this doesn't pick up the 3 repeats!
b = a.asStream; // don't do .all! FIXED!!!! on 1-6-07. We'll need to update the computers!
30.do({b.next.postln}); // why more then 15 elements? Fixed ...
// Prand returns a random value from the list... repeats is number of items that can be returned
// values CAN repeat
a = Prand([1, 2, 3, 4, 5], 10);
a.asStream.all;
a = Pseed(123, Prand([1, 2, 3, 4, 5], 10));
b = a.asStream;
10.do({b.next.postln});
// and again:
b = a.asStream;
10.do({b.next.postln});
// Pxrand avoids the same element repeated twice
a = Pxrand([1, 2, 3, 4, 5], 10);
a.asStream.all;
// Pwrand allows a weighted randomness... weights should add to 1...
a = Pwrand([1, 2, 3,4, 5], [0.1, 0.1, 0.1, 0.1, 0.5], 10);
a.asStream.all;
// normalizeSum can help!
a = Pwrand([1, 2, 3,4, 5], [1, 1, 1, 1, 5].normalizeSum, 10);
a.asStream.all;
/* randomness */
// Pwhite - uniform dist (lo, hi, numrepeats)
a = Pwhite(10, 100, 10);
b = a.asStream;
15.do({b.next.postln}); // only 10 values!
// You can use envelopes as an input to a random pattern ...
// Pwhite - uniform dist (lo, hi, numrepeats)
a = Pwhite(Env([10, 90], [4]).asStream, 100, inf);
b = a.asStream;
b.next.value; // ... must call .value to get the result though
(
Routine.run({
a = Pwhite(Env([10, 90], [4]).asStream, 100, inf);
b = a.asStream;
20.do({
b.next.value.postln;
0.5.wait;
})
})
)
// Pbrown (lo, hi, step, length) where step is the max change per step (defaults to an exprand2)
a = Pbrown(60, 72);
b = a.asStream;
20.do({b.next.postln})
// Pwalk - a List Pattern that is similar to Pbrown... except iterates through a list:
a = Pwalk([60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70], // the list
Prand([3, 2, 1, 0, -1, -2, -3], inf), // the possible steps
Pseq([1, -1], inf), // go forward and back
4); // startpos
b = a.asStream;
20.do({b.next.postln});
// Pfunc - evaluate a given function. Also takes a function to evaluate on .reset
i = 0;
// b
a = Pfunc({
i = i + 1;
(i == 10).if({b.reset}); // if i == 10, reset the Stream
i
}, { // the function to evaluate on reset
i = 0;
// change the function
b.nextFunc_({
i = i + 1;
(i == 10).if({b.reset});
i * 10; // return i * 10
})
});
b = a.asStream;
30.do({b.next.postln})
b=nil;
b.reset
b.next
// Pfuncn - no reset function, but you tell it the number of times to evaluate...
// the function can also be reset at any time
i = 0;
a = Pfuncn({
i = i + 1;
(i * i.rand).postln;
}, 10);
b = a.asStream;
15.do({b.next.postln})
// Here are a few I'm making... These should be available in the main dist soon
// Plprand(lo, hi, length);
a = Plprand(0.0, 1.0, 100);
b = a.asStream;
// post the collection, and check mean
(b.all.postln).sum / 100;
a = Phprand(0.0, 1.0, 100);
b = a.asStream;
(b.all.postln).sum / 100;
a = Pmeanrand(0.0, 1.0, 100);
b = a.asStream;
(b.all.postln).sum / 100;
// Pbeta(lo, hi, prob1, prob2, length) where prob1 is prob of numbers near lo, prob2 is near hi
a = Pbeta(0, 1, 0.2, 0.2, 100);
b = a.asStream;
(b.all.postln).sum / 100;
// Pcauchy(mean, spread, length);
a = Pcauchy(0, 1, 100);
b = a.asStream;
(b.all.postln).sum / 100;
// Pgauss(mean, dev, length);
a = Pgauss(0, 1, 100);
b = a.asStream;
(b.all.postln).sum / 100;
// Ppoisson(mean, length)
a = Ppoisson(1, 100);
b = a.asStream;
(b.all.postln).sum / 100;
// Pexp(lo, hi, length);
a = Pexprand(0.0001, 1, 100);
b = a.asStream;
(b.all.postln).sum / 100;
/* One of my FAVORITE things to do is use an Envelope as a Stream inside a Routine... */
a = CtkNoteObject(
SynthDef(\test, {arg freq, pan = 0;
Out.ar(0,
Pan2.ar(
SinOsc.ar(freq, 0, XLine.kr(0.1, 0.00001, 1, doneAction: 2)),
pan)
)
})
);
Routine.run({
var freqenv, curfreq;
// convert to Pseg, the to Stream
freqenv = Env([440, 880], [10], \exp).asPseg.asStream;
curfreq = freqenv.next;
while({
a.new.freq_(curfreq).play;
1.0.rrand(0.5).wait;
curfreq = freqenv.next;
curfreq.notNil;
})
});
// within the Routine, the stream will keep track of their current time
r = {arg pan;
Routine.run({
var freqenv, curfreq;
// convert to Pseg, the to Stream
freqenv = Env([440, 880], [10], \exp).asPseg.asStream;
curfreq = freqenv.next;
while({
a.new.freq_(curfreq).play;
1.0.rrand(0.5).wait;
curfreq = freqenv.next;
curfreq.notNil;
})
})
};
Routine.run({
5.do({r.value(-1.0.rrand(1.0)); 2.wait})
});
/* Pbind and EventStreams */
/* Pbind takes a set of keywords and values... when .play is used on it, an event is created. This uses the default event SynthDef. It will run until a value comes up nil*/
(
Pbind(
\degree, Pseq([1,3,5,7], 4),
\dur, 0.125,
\octave, 4,
\root, 3
).play
)
/* To use your own SynthDefs, you need to .memStore them rather then .load. Then, reference it through the \instrument keyword in Pbind */
(
SynthDef(\cfstring1, { arg i_out, freq = 360, gate = 1, pan, amp=0.1;
var out, eg, fc, osc, a, b, w;
fc = LinExp.kr(LFNoise1.kr(Rand(0.25,0.4)), -1,1,500,2000);
osc = Mix.fill(8, {LFSaw.ar(freq * [Rand(0.99,1.01),Rand(0.99,1.01)], 0, amp) }).distort * 0.2;
eg = EnvGen.kr(Env.asr(1,1,1), gate, doneAction:2);
out = eg * RLPF.ar(osc, fc, 0.1);
#a, b = out;
Out.ar(i_out, Mix.ar(PanAz.ar(4, [a, b], [pan, pan+0.3])));
}).store;
)
(
e = Pbind(
\degree, Pseq([1,3,5,7], 4), // assumes 0 is middle C
\dur, 0.2,
\instrument, \cfstring1
).play; // returns an EventStream
)
(
e = Pbind(
\degree, Pseq([0,7,4,7], 4),
\dur, 0.2,
\instrument, \cfstring1,
\ctranspose, 1, //up a half step
\octave, 6 // defaults to ocatve 5 - 6 raises it one octave
).play; // returns an EventStream
)
/* notice in the above examples, the 'freq' arg is changed in the resulting synth... but there isn't really anywhere that WE do that directly! This is what frustrates me about Pbind and EventStreams!!! */
/* Streams can be changed as they play */
(
e = Pbind(
\degree, Pseq([0,7,4,7], inf),
\dur, 0.2,
\instrument, \cfstring1,
\ctranspose, 1, //up a half step
\octave, 6 // defaults to ocatve 5 - 6 raises it one octave
).play; // returns an EventStream
)
(
e.stream = Pbind(
\degree, Pseq([0,3,7,10], inf),
\dur, 0.4,
\instrument, \cfstring1,
\ctranspose, 7, //up a half step
\octave, 5 // defaults to ocatve 5 - 6 raises it one octave
).asStream;
)
(
e.stream = Pbind(
\degree, Pseq([0,7,4,7], inf),
\dur, 0.2,
\instrument, \cfstring1,
\ctranspose, 1,
\octave, 6
).asStream;
)
/* here is an example for passing in other values... and more on using patterns for values */
(
SynthDef("acid", { arg out, freq = 1000, gate = 1, pan = 1, cut = 4000, rez = 0.8, amp = 1;
Out.ar(out,
Pan2.ar(
RLPF.ar(
Pulse.ar(freq,0.05),
cut, rez),
pan) * EnvGen.kr(Env.linen(0.01, 1, 0.3), gate, amp, doneAction:2);
)
}).store;
)
(
Pbind(\instrument,\acid, \dur,Pseq([0.25,0.5,0.25],inf), \root,-12,
\degree,Pseq([0,3,5,7,9,11,5,1],inf), \pan,Pfunc({1.0.rand2}),
\cut,Pxrand([1000,500,2000,300],inf), \rez,Pfunc({0.7.rand +0.3}), \amp,0.2).play;
)
/* finally... running parallel streams at once ... polyphony! */
(
// Ppars can be nested
Ppar([
Pbind(
\dur, Prand([0.2, 0.4, 0.6], inf),
\midinote, Prand([72, 74, 76, 77, 79, 81], inf),
\db, -26,
\legato, 1.1
),
Pseq([
Pbind(\dur, 3.2, \freq, Pseq([\rest]) ),
Prand([
Ppar([
Pbind(\dur, 0.2, \pan, 0.5, \midinote, Pseq([60, 64, 67, 64])),
Pbind(\dur, 0.4, \pan, -0.5, \midinote, Pseq([48, 43]))
]),
Ppar([
Pbind(\dur, 0.2, \pan, 0.5, \midinote, Pseq([62, 65, 69, 65])),
Pbind(\dur, 0.4, \pan, -0.5, \midinote, Pseq([50, 45]))
]),
Ppar([
Pbind(\dur, 0.2, \pan, 0.5, \midinote, Pseq([64, 67, 71, 67])),
Pbind(\dur, 0.4, \pan, -0.5, \midinote, Pseq([52, 47]))
])
], 12)
], inf)
], inf).play;
)
/* Reading
[Pattern]
[Stream]
[Streams] an overview of the whole Pattern and Stream system!
*/