1 title:: Understanding Streams, Patterns and Events - Part 3
3 related:: Tutorials/Streams-Patterns-Events1, Tutorials/Streams-Patterns-Events2, Tutorials/Streams-Patterns-Events4, Tutorials/Streams-Patterns-Events5, Tutorials/Streams-Patterns-Events6, Tutorials/Streams-Patterns-Events7
4 categories:: Streams-Patterns-Events>Understanding-Streams-Patterns-and-Events
8 ListPatterns are link::Classes/Pattern::s that iterate over arrays of objects in some fashion. All ListPatterns have in common the instance variables list and repeats. The list variable is some link::Classes/Array:: to be iterated over. The repeats variable is some measure of the number of times to do something, whose meaning varies from subclass to subclass. The default value for repeats is 1.
10 A link::Classes/Pseq:: is a Pattern that cycles over a list of values. The repeats variable gives the number of times to repeat the entire list.
13 //////////////////////////////////////////////////////////////
14 // Note: This SynthDef used throughout this document
17 SynthDef( \help_SPE3_SimpleSine, {
18 arg freq, sustain=1.0;
20 osc = SinOsc.ar( [freq,freq+0.05.rand] ) * EnvGen.ar(
21 Env.perc, doneAction: 2, levelScale: 0.3, timeScale: sustain
26 //////////////////////////////////////////////////////////////
30 a = Pseq.new(#[1, 2, 3], 2); // repeat twice
32 7.do({ b.next.postln; });
36 Pseq also has an offset argument which gives a starting offset into the list.
41 a = Pseq.new(#[1, 2, 3, 4], 3, 2); // repeat 3, offset 2
43 13.do({ b.next.postln; });
47 You can pass a function for the repeats variable that gets evaluated when the stream is created.
52 a = Pseq.new(#[1, 2], { rrand(1, 3) }); // repeat 1,2, or 3 times
54 7.do({ b.next.postln; });
58 If you specify the value code::inf:: for the repeats variable, then it will repeat indefinitely.
63 a = Pseq.new(#[1, 2, 3], inf); // infinite repeat
65 10.do({ b.next.postln; });
69 Pseq used as a sequence of pitches:
71 Remember that math operations like code::midicps:: can be used on streams.
73 The alternative code::Pseq(...).midicps.asStream:: is also possible because both pattern and stream inherit from link::Classes/AbstractFunction:: for which midicps is a method. ( midicps converts a midi value to cycles per second or Hz )
78 a = Pseq(#[60, 61, 63, 65, 67, 63], inf ).asStream.midicps;
82 Synth(\help_SPE3_SimpleSine, [ \freq, a.next, \sustain, d ]);
89 link::Classes/Pser:: is like Pseq, however the repeats variable gives the number of items returned instead of the number of complete cycles.
94 a = Pser.new(#[1, 2, 3], 5); // return 5 items
96 6.do({ b.next.postln; });
100 link::Classes/Prand:: returns one item from the list at random for each repeat.
105 a = Prand.new(#[1, 2, 3, 4, 5], 6); // return 6 items
107 7.do({ b.next.postln; });
111 Prand used as a sequence of pitches:
116 a = Prand(#[60, 61, 63, 65], inf).midicps.asStream;
120 Synth(\help_SPE3_SimpleSine,[\freq, a.next]);
127 link::Classes/Pxrand::, like Prand, returns one item from the list at random for each repeat, but Pxrand never repeats the same element twice in a row.
132 a = Pxrand.new(#[1, 2, 3], 10); // return 10 items
134 11.do({ b.next.postln; });
138 Pxrand used as a sequence of pitches:
143 a = Pxrand(#[60, 61, 63, 65], inf).midicps.asStream;
146 Synth(\help_SPE3_SimpleSine, [\freq, a.next]);
153 link::Classes/Pshuf:: iterates over the list in scrambled order. The entire scrambled list is repeated in the same order the number of times given by the repeats variable.
158 a = Pshuf.new(#[1, 2, 3, 4], 3);
160 13.do({ b.next.postln; });
164 Pshuf used as a sequence of pitches:
169 a = Pshuf(#[60, 61, 65, 67], inf).midicps.asStream;
172 Synth(\help_SPE3_SimpleSine,[\freq, a.next]);
179 section::Nesting Patterns
181 If a link::Classes/Pattern:: encounters another Pattern in its list, it embeds that pattern in its output. That is, it creates a stream on that pattern and iterates that pattern until it ends before moving on.
183 For example here is one pattern nested in another.
188 a = Pseq.new([1, Pseq.new([100,200], 2), 3], 3);
190 19.do({ b.next.postln; });
194 Pseqs nested in a Prand:
205 13.do({ b.next.postln; });
209 Nested sequences of pitches:
215 Pseq(#[60, 61, 63, 65, 67, 63]),
216 Prand(#[72, 73, 75, 77, 79], 6),
217 Pshuf(#[48, 53, 55, 58], 2)
222 Synth( \help_SPE3_SimpleSine, [\freq, a.next] );
229 section::Math operations on ListPatterns
231 Pattern code::b:: plays pattern a once normally, once transposed up a fifth and once transposed up a fourth.
236 a = Pseq(#[60, 62, 63, 65, 67, 63]);
237 b = Pseq([ a, a + 7, a + 5], inf).asStream;
240 Synth(\help_SPE3_SimpleSine, [ \freq, b.next.midicps ]);
247 Adding two patterns together. The second pattern transposes each fifth note of the first pattern down an octave.
252 a = Pseq(#[60, 62, 63, 65, 67, 63], inf) + Pseq(#[0, 0, 0, 0, -12], inf);
253 a = a.asStream.midicps;
256 Synth(\help_SPE3_SimpleSine,[\freq, a.next]);
263 section::Making Music with ListPatterns
265 Here is the same example given in part 2 rewritten to use ListPatterns. It uses nested patterns and results in much more concise code. SuperCollider allows you to write code::SomeClass.new(params):: as code::SomeClass(params):: eliminating the ".new". This can make code like the pattern examples below, which create a lot of objects, more readable.
269 SynthDef( \help_SPE3_Allpass6, { arg freq;
272 LFSaw.ar( freq, mul: EnvGen.kr( Env.perc, levelScale: 0.3, doneAction: 2 ) ),
273 LFNoise1.kr(1, 36, 110).midicps,
276 6.do({ out = AllpassN.ar(out, 0.05, [0.05.rand, 0.05.rand], 4) });
286 nil, // a nil item reached in a pattern causes it to end
287 Pseq(#[24, 31, 36, 43, 48, 55]);
289 Pseq([ 60, Prand(#[63, 65]), 67, Prand(#[70, 72, 74]) ], { rrand(2, 5) }),
290 Prand(#[74, 75, 77, 79, 81], { rrand(3, 9) })
291 ], inf).asStream.midicps;
295 Synth( \help_SPE3_Allpass6, [\freq, freqStream.next ]);
302 Here is an example that uses a Pattern to create a rhythmic solo. The values in the pattern specify the amplitudes of impulses fed to the link::Classes/Decay2:: generator.
306 SynthDef( \help_SPE3_Mridangam, { arg t_amp;
310 WhiteNoise.ar(70) * Decay2.kr( t_amp, 0.002, 0.1 ),
317 DetectSilence.ar( out, doneAction: 2 );
320 SynthDef( \help_SPE3_Drone, {
323 Saw.ar([60, 60.04].midicps)
325 Saw.ar([67, 67.04].midicps),
334 // percussion solo in 10/8
336 var stream, pat, amp;
342 Pseq(#[0.9, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], 2),
343 Pseq(#[0.9, 0.0, 0.0, 0.2, 0.0, 0.0, 0.0, 0.2, 0.0, 0.0], 2),
344 Pseq(#[0.9, 0.0, 0.0, 0.2, 0.0, 0.2, 0.0, 0.2, 0.0, 0.0], 2),
345 Pseq(#[0.9, 0.0, 0.0, 0.2, 0.0, 0.0, 0.0, 0.2, 0.0, 0.2], 2),
349 Pseq(#[0.9, 0.0, 0.0, 0.7, 0.0, 0.2, 0.0, 0.7, 0.0, 0.0]),
350 Pseq(#[0.9, 0.2, 0.0, 0.7, 0.0, 0.2, 0.0, 0.7, 0.0, 0.0]),
351 Pseq(#[0.9, 0.0, 0.0, 0.7, 0.0, 0.2, 0.0, 0.7, 0.0, 0.2]),
352 Pseq(#[0.9, 0.0, 0.0, 0.7, 0.2, 0.2, 0.0, 0.7, 0.0, 0.0]),
353 Pseq(#[0.9, 0.0, 0.0, 0.7, 0.0, 0.2, 0.2, 0.7, 0.2, 0.0]),
354 Pseq(#[0.9, 0.2, 0.2, 0.7, 0.2, 0.2, 0.2, 0.7, 0.2, 0.2]),
355 Pseq(#[0.9, 0.2, 0.2, 0.7, 0.2, 0.2, 0.2, 0.7, 0.0, 0.0]),
356 Pseq(#[0.9, 0.0, 0.0, 0.7, 0.2, 0.2, 0.2, 0.7, 0.0, 0.0]),
357 Pseq(#[0.9, 0.0, 0.4, 0.0, 0.4, 0.0, 0.4, 0.0, 0.4, 0.0]),
358 Pseq(#[0.9, 0.0, 0.0, 0.4, 0.0, 0.0, 0.4, 0.2, 0.4, 0.2]),
359 Pseq(#[0.9, 0.0, 0.2, 0.7, 0.0, 0.2, 0.0, 0.7, 0.0, 0.0]),
360 Pseq(#[0.9, 0.0, 0.0, 0.7, 0.0, 0.0, 0.0, 0.7, 0.0, 0.0]),
361 Pseq(#[0.9, 0.7, 0.7, 0.0, 0.0, 0.2, 0.2, 0.2, 0.0, 0.0]),
362 Pseq(#[0.9, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])
365 // tehai : 7 beat motif 3 times sharing 1st beat with next 7x3
366 // and again the third time:
367 // 123456712345671234567 123456712345671234567
368 // 123456712345671234567
370 // 1234567890123456789012345678901234567890123456789012345678901
371 Pseq(#[2.0, 0.0, 0.2, 0.5, 0.0, 0.2, 0.9,
372 1.5, 0.0, 0.2, 0.5, 0.0, 0.2, 0.9,
373 1.5, 0.0, 0.2, 0.5, 0.0, 0.2], 3),
374 Pseq(#[5], 1), // sam
379 stream = pat.asStream;
382 Synth(\help_SPE3_Drone);
384 if( ( amp = stream.next ) > 0,
385 { Synth(\help_SPE3_Mridangam, [ \t_amp, amp ]) }
393 To go to the next file:
394 link::Tutorials/Streams-Patterns-Events4::