title:: Understanding Streams, Patterns and Events - Part 3
summary:: ListPatterns
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
categories:: Tutorials>Streams-Patterns-Events

section::ListPatterns

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.

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.

code::
//////////////////////////////////////////////////////////////
// Note: This SynthDef used throughout this document
(
s = Server.local;
SynthDef( \help_SPE3_SimpleSine, {
	arg freq, sustain=1.0;
	var osc;
	osc = SinOsc.ar( [freq,freq+0.05.rand] ) * EnvGen.ar(
		Env.perc, doneAction: 2, levelScale: 0.3, timeScale: sustain
	);
	Out.ar(0,osc);
}).add;
)
//////////////////////////////////////////////////////////////

(
var a, b;
a = Pseq.new(#[1, 2, 3], 2);	// repeat twice
b = a.asStream;
7.do({ b.next.postln; });
)
::

Pseq also has an offset argument which gives a starting offset into the list.

code::
(
var a, b;
a = Pseq.new(#[1, 2, 3, 4], 3, 2);	// repeat 3, offset 2
b = a.asStream;
13.do({ b.next.postln; });
)
::

You can pass a function for the repeats variable that gets evaluated when the stream is created.

code::
(
var a, b;
a = Pseq.new(#[1, 2], { rrand(1, 3) });	// repeat 1,2, or 3 times
b = a.asStream;
7.do({ b.next.postln; });
)
::

If you specify the value code::inf:: for the repeats variable, then it will repeat indefinitely.

code::
(
var a, b;
a = Pseq.new(#[1, 2, 3], inf);	// infinite repeat
b = a.asStream;
10.do({ b.next.postln; });
)
::

Pseq used as a sequence of pitches:

Remember that math operations like code::midicps:: can be used on streams.

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 )

code::
(
var a, d;
a = Pseq(#[60, 61, 63, 65, 67, 63], inf ).asStream.midicps;
d = 0.3;
Task({
	12.do({
		Synth(\help_SPE3_SimpleSine, [ \freq, a.next, \sustain, d ]);
		d.wait;
	});
}).play
)
::

link::Classes/Pser:: is like Pseq, however the repeats variable gives the number of items returned instead of the number of complete cycles.

code::
(
var a, b;
a = Pser.new(#[1, 2, 3], 5);	// return 5 items
b = a.asStream;
6.do({ b.next.postln; });
)
::

link::Classes/Prand:: returns one item from the list at random for each repeat.

code::
(
var a, b;
a = Prand.new(#[1, 2, 3, 4, 5], 6);	// return 6 items
b = a.asStream;
7.do({ b.next.postln; });
)
::

Prand used as a sequence of pitches:

code::
(
var a, d;
a = Prand(#[60, 61, 63, 65], inf).midicps.asStream;
d = 0.3;
Task({
	12.do({
		Synth(\help_SPE3_SimpleSine,[\freq, a.next]);
		d.wait;
	});
}).play;
)
::

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.

code::
(
var a, b;
a = Pxrand.new(#[1, 2, 3], 10);	// return 10 items
b = a.asStream;
11.do({ b.next.postln; });
)
::

Pxrand used as a sequence of pitches:

code::
(
var a;
a = Pxrand(#[60, 61, 63, 65], inf).midicps.asStream;
Task({
	12.do({
		Synth(\help_SPE3_SimpleSine, [\freq, a.next]);
		0.8.wait;
	});
}).play;
)
::

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.

code::
(
var a, b;
a = Pshuf.new(#[1, 2, 3, 4], 3);
b = a.asStream;
13.do({ b.next.postln; });
)
::

Pshuf used as a sequence of pitches:

code::
(
var a, b;
a = Pshuf(#[60, 61, 65, 67], inf).midicps.asStream;
Task({
	12.do({
		Synth(\help_SPE3_SimpleSine,[\freq, a.next]);
		0.5.wait;
	});
}).play;
)
::

section::Nesting Patterns

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.

For example here is one pattern nested in another.

code::
(
var a, b;
a = Pseq.new([1, Pseq.new([100,200], 2), 3], 3);
b = a.asStream;
19.do({ b.next.postln; });
)
::

Pseqs nested in a Prand:

code::
(
var a, b;
a = Prand.new([
		Pseq.new([1, 2], 2),
		Pseq.new([3, 4], 2),
		Pseq.new([5, 6], 2)
	], 3);
b = a.asStream;
13.do({ b.next.postln; });
)
::

Nested sequences of pitches:

code::
(
var a;
a = Prand([
		Pseq(#[60, 61, 63, 65, 67, 63]),
		Prand(#[72, 73, 75, 77, 79], 6),
		Pshuf(#[48, 53, 55, 58], 2)
	], inf
).midicps.asStream;
Task({
	loop({
		Synth( \help_SPE3_SimpleSine, [\freq, a.next] );
		0.3.wait;
	});
}).play;
)
::

section::Math operations on ListPatterns

Pattern code::b:: plays pattern a once normally, once transposed up a fifth and once transposed up a fourth.

code::
(
var a, b;
a = Pseq(#[60, 62, 63, 65, 67, 63]);
b = Pseq([ a, a + 7, a + 5], inf).asStream;
Task({
	24.do({
		Synth(\help_SPE3_SimpleSine, [ \freq, b.next.midicps ]);
		0.3.wait;
	});
}).play;
)
::

Adding two patterns together. The second pattern transposes each fifth note of the first pattern down an octave.

code::
(
var a;
a = Pseq(#[60, 62, 63, 65, 67, 63], inf) + Pseq(#[0, 0, 0, 0, -12], inf);
a = a.asStream.midicps;
Task({
	25.do({
		Synth(\help_SPE3_SimpleSine,[\freq, a.next]);
		0.3.wait;
	});
}).play;
)
::

section::Making Music with ListPatterns

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.

code::
(
SynthDef( \help_SPE3_Allpass6, { arg freq;
	var out, env;
	out = RLPF.ar(
		LFSaw.ar( freq, mul: EnvGen.kr( Env.perc, levelScale: 0.3, doneAction: 2 ) ),
		LFNoise1.kr(1, 36, 110).midicps,
		0.1
	);
	6.do({ out = AllpassN.ar(out, 0.05, [0.05.rand, 0.05.rand], 4) });
	Out.ar( 0, out );
}).add
)

(
var freqStream;

freqStream = Pseq([
	Prand([
		nil,	// a nil item reached in a pattern causes it to end
		Pseq(#[24, 31, 36, 43, 48, 55]);
	]),
	Pseq([ 60, Prand(#[63, 65]), 67, Prand(#[70, 72, 74]) ], { rrand(2, 5) }),
	Prand(#[74, 75, 77, 79, 81], { rrand(3, 9) })
], inf).asStream.midicps;

Task({
	loop({
		Synth( \help_SPE3_Allpass6, [\freq, freqStream.next ]);
		0.13.wait;
	});
}).play;
)
::

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.

code::
(
SynthDef( \help_SPE3_Mridangam, { arg t_amp;
	var out;

	out = Resonz.ar(
		WhiteNoise.ar(70) * Decay2.kr( t_amp, 0.002, 0.1 ),
		60.midicps,
		0.02,
		4
	).distort * 0.4;

	Out.ar( 0, out );
	DetectSilence.ar( out, doneAction: 2 );
}).add;

SynthDef( \help_SPE3_Drone, {
	var out;
	out = LPF.ar(
		Saw.ar([60, 60.04].midicps)
		+
		Saw.ar([67, 67.04].midicps),
		108.midicps,
		0.007
	);
	Out.ar( 0, out );
}).add;
)

(
// percussion solo in 10/8

var stream, pat, amp;

pat = Pseq([
	Pseq(#[0.0], 10),

	// intro
	Pseq(#[0.9, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], 2),
	Pseq(#[0.9, 0.0, 0.0, 0.2, 0.0, 0.0, 0.0, 0.2, 0.0, 0.0], 2),
	Pseq(#[0.9, 0.0, 0.0, 0.2, 0.0, 0.2, 0.0, 0.2, 0.0, 0.0], 2),
	Pseq(#[0.9, 0.0, 0.0, 0.2, 0.0, 0.0, 0.0, 0.2, 0.0, 0.2], 2),

	// solo
	Prand([
		Pseq(#[0.9, 0.0, 0.0, 0.7, 0.0, 0.2, 0.0, 0.7, 0.0, 0.0]),
		Pseq(#[0.9, 0.2, 0.0, 0.7, 0.0, 0.2, 0.0, 0.7, 0.0, 0.0]),
		Pseq(#[0.9, 0.0, 0.0, 0.7, 0.0, 0.2, 0.0, 0.7, 0.0, 0.2]),
		Pseq(#[0.9, 0.0, 0.0, 0.7, 0.2, 0.2, 0.0, 0.7, 0.0, 0.0]),
		Pseq(#[0.9, 0.0, 0.0, 0.7, 0.0, 0.2, 0.2, 0.7, 0.2, 0.0]),
		Pseq(#[0.9, 0.2, 0.2, 0.7, 0.2, 0.2, 0.2, 0.7, 0.2, 0.2]),
		Pseq(#[0.9, 0.2, 0.2, 0.7, 0.2, 0.2, 0.2, 0.7, 0.0, 0.0]),
		Pseq(#[0.9, 0.0, 0.0, 0.7, 0.2, 0.2, 0.2, 0.7, 0.0, 0.0]),
		Pseq(#[0.9, 0.0, 0.4, 0.0, 0.4, 0.0, 0.4, 0.0, 0.4, 0.0]),
		Pseq(#[0.9, 0.0, 0.0, 0.4, 0.0, 0.0, 0.4, 0.2, 0.4, 0.2]),
		Pseq(#[0.9, 0.0, 0.2, 0.7, 0.0, 0.2, 0.0, 0.7, 0.0, 0.0]),
		Pseq(#[0.9, 0.0, 0.0, 0.7, 0.0, 0.0, 0.0, 0.7, 0.0, 0.0]),
		Pseq(#[0.9, 0.7, 0.7, 0.0, 0.0, 0.2, 0.2, 0.2, 0.0, 0.0]),
		Pseq(#[0.9, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0])
	], 30),

	// tehai : 7 beat motif 3 times sharing 1st beat with next 7x3
	// and again the third time:
	//   123456712345671234567                   123456712345671234567
	//                       123456712345671234567
	//   !                   !                   !                   !
	//   1234567890123456789012345678901234567890123456789012345678901
	Pseq(#[2.0, 0.0, 0.2, 0.5, 0.0, 0.2, 0.9,
		1.5, 0.0, 0.2, 0.5, 0.0, 0.2, 0.9,
		1.5, 0.0, 0.2, 0.5, 0.0, 0.2], 3),
	Pseq(#[5], 1),	// sam

	Pseq(#[0.0], inf)
]);

stream = pat.asStream;

Task({
	Synth(\help_SPE3_Drone);
	loop({
		if( ( amp = stream.next ) > 0,
			{ Synth(\help_SPE3_Mridangam, [ \t_amp, amp ]) }
		);
		(1/8).wait;
	})
}).play
)
::

To go to the next file:
link::Tutorials/Streams-Patterns-Events4::
