File: PG_06c_Composition_of_Patterns.schelp

package info (click to toggle)
supercollider 1%3A3.13.0%2Brepack-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 80,292 kB
  • sloc: cpp: 476,363; lisp: 84,680; ansic: 77,685; sh: 25,509; python: 7,909; makefile: 3,440; perl: 1,964; javascript: 974; xml: 826; java: 677; yacc: 314; lex: 175; objc: 152; ruby: 136
file content (118 lines) | stat: -rw-r--r-- 6,177 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
title:: Pattern Guide 06c: Composition of Patterns
summary:: Making multiple event patterns act as one
related:: Tutorials/A-Practical-Guide/PG_06b_Time_Based_Patterns, Tutorials/A-Practical-Guide/PG_06d_Parallel_Patterns
categories:: Streams-Patterns-Events>A-Practical-Guide

section::Adding values to a base event pattern (Or, "Pattern Composition")

One way to use patterns is to write everything into the pattern up front. This has the advantage of clarity and ease of understanding. Another way is to modularize the behavior by creating smaller, simpler patterns and combining their results into single events that have keys and values from all the component patterns.

This is related to the computer science concept of "function composition," in which a complex calculation can be written not as a single large function, but as several smaller functions that are then chained together into a single function. Since Functions are normal objects in SuperCollider, it's easy to do an operation on a function that returns a composite function (which may then be used like any other function). http://en.wikipedia.org/wiki/Function_composition_(computer_science)

In mathematics, the code::·:: operator represents function composition.

code::
f(x) = x + 1
g(x) = x * 2

g · f = g(f(x)) = (x + 1) * 2
::

code::g · f:: means to evaluate code::f:: first, then pass its result to code::g::. The code::·:: operator is written as code::<>:: in SuperCollider.

code::
f = { |x| x + 1 };
g = { |x| x * 2 };

h = (g <> f);
--> a Function

h.value(1);
--> 4	// == (1+1) * 2

(f <> g).value(1)
--> 3	// == (1*2) + 1

// g · f == g(f(x)) -- f is evaluated first, and its result is passed to g
g.value(f.value(1));
--> 4
::

Event patterns can be similarly composed.

definitionList::
## code::Pbindf(pattern, pairs):: || Adds new key-value pairs onto a pre-existing Pbind-style pattern. code::Pbindf(Pbind(\a, patternA), \b, patternB, \c, patternC):: gets the same result as code::Pbind(\a, patternA, \b, patternB, \c, patternC):: .
## code::Pchain(patterns):: || Chains separate Pbind-style patterns together, so that all their key-value pairs go into the same event. For example, if one part of your code creates a Pbind instance code::a = Pbind(\a, patternA):: and another part creates code::b = Pbind(\b, patternB, \c, patternC)::, you could append code::\b:: and code::\c:: into the code::\a:: result using code::Pchain(b, a):: . The subpatterns evaluate in reverse order, in keeping with function composition notation.

For musical purposes, you could have one part of your code create a pattern defining rhythm and another part defining pitch material, then combine them with link::Classes/Pchain::.

code::
~rhythm = Pbind(
	\dur, Pwrand(#[0.125, 0.25, 0.5], #[0.3, 0.5, 0.2], inf),
	\legato, Pwrand(#[0.1, 0.6, 1.01], #[0.1, 0.3, 0.6], inf)
);
~melody = Pbind(
	\degree, Pwhite(-4, 11, inf)
);

p = Pchain(~melody, ~rhythm).play;
p.stop;
::

That in itself has some good potential for algorithmic composition. Introducing link::Classes/EventPatternProxy:: into the mix makes it possible to swap different melody and rhythm components in and out on the fly, with no interruption. We can even change the type of pattern ( link::Classes/Pbind::, link::Classes/Pmono::, link::Classes/PmonoArtic:: ) with no ill effect.

code::
~rhythm = EventPatternProxy(Pbind(
	\dur, Pwrand(#[0.125, 0.25, 0.5], #[0.3, 0.5, 0.2], inf),
	\legato, Pwrand(#[0.1, 0.6, 1.01], #[0.1, 0.3, 0.6], inf)
));

~melody = EventPatternProxy(Pbind(
	\degree, Pwhite(-4, 11, inf)
));

p = Pchain(~melody, ~rhythm).play;

~melody.source = PmonoArtic(\default, \degree, Pseries(4, Prand(#[-1, 1], inf), inf).fold(-4, 11));

~melody.source = Pbind(\degree, Pseries(4, Pwrand(#[-2, -1, 1, 2], #[0.3, 0.2, 0.2, 0.3], inf), inf).fold(-4, 11));

p.stop;
::
::

subsection::Pset and cousins

A group of pattern classes allow single event keys to be overwritten, or one addition or multiplication to be performed. link::Classes/Pkey::, in combination with the link::Classes/Pchain:: or link::Classes/Pbindf:: "pattern composition" classes, can do everything the following classes can do (though this alternate notation may be more convenient in certain cases).

definitionList::
## code::Pset(name, value, pattern):: || Get one event from code::pattern::, and then put the next value from the code::value:: pattern into the code::name:: key. If the source pattern specifies a value for the same name, the value from the source will be replaced with the new one.
## code::Padd(name, value, pattern):: || After getting the next event, replace the code::name:: value with its existing value code::+:: the next number from code::value::.
## code::Pmul(name, value, pattern):: || After getting the next event, replace the code::name:: value with its existing value code::*:: the next number from code::value::.
::

These patterns remain in the library mainly for reasons of backward compatibility, since their behavior can be replicated easily using link::Classes/Pbindf::.

table::
## code::Pset(name, value, pattern):: || code::Pbindf(pattern, name, value)::
## code::Padd(name, value, pattern):: || code::Pbindf(pattern, name, Pkey(name) + value)::
## code::Pmul(name, value, pattern):: || code::Pbindf(pattern, name, Pkey(name) * value)::
::

The patterns link::Classes/Psetpre::, link::Classes/Paddpre::, and link::Classes/Pmulpre:: reverse the order of evaluation. link::Classes/Pchain:: is able to duplicate this functionality.

definitionList::
## code::Psetpre(name, value, pattern):: || Get the next code::value:: and put it into the event prototype before evaluating code::pattern::.
::

table::
## code::Psetpre(name, value, pattern):: || code::Pchain(pattern, Pbind(name, value))::
## code::Paddpre(name, value, pattern):: || code::Pchain(pattern, Pbind(name, Pkey(name) + value))::
::
Similar for code::Pmulpre::.

A third group -- link::Classes/Psetp::, link::Classes/Paddp::, link::Classes/Pmulp:: -- behave slightly differently, nesting pattern evaluation.

Previous:	link::Tutorials/A-Practical-Guide/PG_06b_Time_Based_Patterns::

Next:		link::Tutorials/A-Practical-Guide/PG_06d_Parallel_Patterns::