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 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247
|
title:: Pattern Guide 04: Words to Phrases
summary:: Nesting patterns, arranging music in terms of phrases
related:: Tutorials/A-Practical-Guide/PG_03_What_Is_Pbind, Tutorials/A-Practical-Guide/PG_05_Math_on_Patterns
categories:: Streams-Patterns-Events>A-Practical-Guide
section::From words to phrases
A single pattern by itself is not so exciting. But patterns can be used together to get more complex results.
subsection::Patterns within list patterns
We saw list patterns ( link::Classes/Pseq::, link::Classes/Prand::, etc.) that returned numbers from a preset list, either in the order given or rearranged randomly. The list may also include other patterns. When a list pattern encounters another pattern in its list, the inner pattern is emphasis::embedded:: into the stream. That is, the inner pattern takes over until it runs out of values to return. Then, control returns to the outer list pattern. This is like calling a function in the middle of another function.
There is no preset limit to the number of levels of embedding.
If a single pattern is like a word, a list pattern that uses other patterns could be more like a sentence or phrase. You can alternate between different behaviors, either in a predictable order as in the example below, or randomly by using one of the random-order list patterns.
code::
// Scale segments, in the sequence: up, up, down (repeat)
(
TempoClock.default.tempo = 1;
p = Pbind(
\degree, Pseq([
Pseries({ rrand(0, 7) }, 1, { rrand(4, 8) }), // up (step = 1)
Pseries({ rrand(0, 7) }, 1, { rrand(4, 8) }), // up (step = 1)
Pseries({ rrand(7, 14) }, -1, { rrand(4, 8) }) // down (step = -1)
], inf),
\dur, 0.125
).play;
)
p.stop;
::
But it gets even more fun -- list patterns don't care whether they're enclosing value patterns (as in the previous example) or event patterns. That means you can write a set of Pbind-style patterns, each one representing a phrase, and string them together. This next example is longer, but that's only because of a larger number of phrase patterns. The structure is very simple, though: code::Pxrand([Pbind(), Pmono(), Pmono()...], inf):: . Some of the phrases are written with Pmono to slide between notes.
code::
(
SynthDef(\bass, { |out, freq = 440, gate = 1, amp = 0.5, slideTime = 0.17, ffreq = 1100, width = 0.15,
detune = 1.005, preamp = 4|
var sig,
env = Env.adsr(0.01, 0.3, 0.4, 0.1);
freq = Lag.kr(freq, slideTime);
sig = Mix(VarSaw.ar([freq, freq * detune], 0, width, preamp)).distort * amp
* EnvGen.kr(env, gate, doneAction: Done.freeSelf);
sig = LPF.ar(sig, ffreq);
Out.ar(out, sig ! 2)
}).add;
)
(
TempoClock.default.tempo = 132/60;
p = Pxrand([
Pbind( // repeated notes
\instrument, \bass,
\midinote, 36,
\dur, Pseq([0.75, 0.25, 0.25, 0.25, 0.5], 1),
\legato, Pseq([0.9, 0.3, 0.3, 0.3, 0.3], 1),
\amp, 0.5, \detune, 1.005
),
Pmono(\bass, // octave jump
\midinote, Pseq([36, 48, 36], 1),
\dur, Pseq([0.25, 0.25, 0.5], 1),
\amp, 0.5, \detune, 1.005
),
Pmono(\bass, // tritone jump
\midinote, Pseq([36, 42, 41, 33], 1),
\dur, Pseq([0.25, 0.25, 0.25, 0.75], 1),
\amp, 0.5, \detune, 1.005
),
Pmono(\bass, // diminished triad
\midinote, Pseq([36, 39, 36, 42], 1),
\dur, Pseq([0.25, 0.5, 0.25, 0.5], 1),
\amp, 0.5, \detune, 1.005
)
], inf).play(quant: 1);
)
p.stop;
::
strong::Shortcut notation:: : Just like you can concatenate arrays with ++, you can also concatenate patterns the same way. Writing code::pattern1 ++ pattern2:: is the same as writing code::Pseq([pattern1, pattern2], 1):: .
subsection::Some ways to string together patterns
definitionList::
## Sequentially || Each sub-pattern follows the next in the same order every time. Use link::Classes/Pseq:: or link::Classes/Pser::.
## Randomized order || Sub-patterns in completely random order ( link::Classes/Prand:: ), random order with no repeats ( link::Classes/Pxrand:: ), or random order according to a set of probabilities ( link::Classes/Pwrand:: ). link::Classes/Pshuf:: creates one random ordering and uses it repeatedly.
## Direct array indexing || Patterns can be chosen in arbitrary order by index. This gives you more control than Pwrand. Both link::Classes/Pindex:: and link::Classes/Pswitch:: can be used for this.
code::
// scale degree segments, every fifth choice is odd-numbered only (descending)
(
var n = 10,
scaleSegments = Array.fill(n, { |i|
if(i.odd) {
Pseries(11, -1, rrand(5, 10))
} {
Pseries(rrand(-4, 4), 1, i+2)
}
});
TempoClock.default.tempo = 1;
p = Pbind(
\degree, Pswitch(scaleSegments, Pseq([Pwhite(0, n-1, 4), Pwhite(0, n-1, 1).select(_.odd)], inf)),
\dur, 0.125
).play;
)
p.stop;
::
## Finite state machine (Pfsm, Pdfsm) || A finite state machine is a way of associating an item with its possible successors. It is closer to a "grammar" than purely random selection. link::Classes/Pfsm:: defines a finite state machine as a set of possible "entry points," followed by a list of the possible "states" of the machine and, for each state, a list of the possible states that may follow the current state. States may be single values or patterns, meaning that phrases can be linked to other phrases that "make sense" in succession (and unwanted transitions can be prevented).
If this sounds a bit like a Markov chain, that's because the Pfsm implementation is a special case of a Markov chain where there is an equal probability of choosing the next state from the valid successors. In a Markov chain, the probabilities are weighted according to analysis of a real-world data stream.
The Pfsm help file includes very good examples of organizing single values and pattern phrases. Also see link::Tutorials/A-Practical-Guide/PG_Cookbook06_Phrase_Network:: for an application of Pfsm to generate a corny jazz solo.
The name link::Classes/Pdfsm:: stands for "deterministic finite state machine," where there is no random selection.
- strong::Third-party extension alert:: : A good Markov chain implementation for SuperCollider exists in the MathLib quark.
::
subsection::Library of named sub-patterns
One very effective way to manage phrases is to make a library, or more precisely link::Classes/Dictionary::, of sub-patterns, and then call them up one at a time. link::Classes/Psym:: is the pattern to do this. The advantage here is that you can store the phrases in a separate place, while the pattern that you actually play is much simpler and describes the musical intent at a much higher level.
code::
// Uses the bass SynthDef above
(
~phrases = (
repeated: Pbind(
\instrument, \bass,
\midinote, 36,
\dur, Pseq([0.75, 0.25, 0.25, 0.25, 0.5], 1),
\legato, Pseq([0.9, 0.3, 0.3, 0.3, 0.3], 1),
\amp, 0.5, \detune, 1.005
),
octave: Pmono(\bass,
\midinote, Pseq([36, 48, 36], 1),
\dur, Pseq([0.25, 0.25, 0.5], 1),
\amp, 0.5, \detune, 1.005
),
tritone: Pmono(\bass,
\midinote, Pseq([36, 42, 41, 33], 1),
\dur, Pseq([0.25, 0.25, 0.25, 0.75], 1),
\amp, 0.5, \detune, 1.005
),
dim: Pmono(\bass,
\midinote, Pseq([36, 39, 36, 42], 1),
\dur, Pseq([0.25, 0.5, 0.25, 0.5], 1),
\amp, 0.5, \detune, 1.005
)
);
TempoClock.default.tempo = 128/60;
// the higher level control pattern is really simple now
p = Psym(Pxrand(#[repeated, octave, tritone, dim], inf), ~phrases).play;
)
p.stop;
::
A complicated pattern with lots of embedding can be hard to read because it's more work to separate note-level details from the larger structure. The pattern choosing the phrases -- code::Pxrand(#[repeated, octave, tritone, dim], inf) :: -- is self-explanatory, however, and Psym fills in the details transparently.
note::
Because of some special handling needed for event patterns, there are two versions of Psym. link::Classes/Psym:: handles event patterns, while link::Classes/Pnsym:: is for value patterns. Think of it this way: Pbind can be contained within Psym, but it contains Pnsym.
code::
( Psym ( Pbind ( Pnsym ) ) )
::
definitionList::
## Good: ||
list::
## code::Psym(**, (pattern1: Pbind(**)) ::
## code::Pbind(\someValue, Pnsym(**, (pattern1: Pwhite(**))) ::
::
## Bad: ||
list::
## code::Pbind(\someValue, Psym(**, (pattern1: Pwhite(**))) ::
::
::
::
subsection::Switching between patterns for individual values
In the examples above, if a list pattern encounters another pattern in its input values, the subpattern is embedded in its entirety before the list pattern is allowed to continue. Sometimes you might want to get just one value out of the subpattern, and then choose a different subpattern on the next event. Pswitch, Psym and Pnsym have cousins that do exactly this: link::Classes/Pswitch1::, Psym1 and Pnsym1.
code::
// random pitches in two distinct ranges; use a coin toss to decide which for this event
// 70% low, 30% high
(
TempoClock.default.tempo = 1;
p = Pbind(
\degree, Pswitch1([Pwhite(7, 14, inf), Pwhite(-7, 0, inf)], Pfunc { 0.7.coin.binaryValue }),
\dur, 0.25
).play;
)
p.stop;
::
Compare to the following:
code::
(
p = Pbind(
\degree, Pswitch([Pwhite(7, 14, inf), Pwhite(-7, 0, inf)], Pfunc { 0.7.coin.binaryValue }),
\dur, 0.25
).play;
)
p.stop;
::
With Pswitch, one of the items is chosen from the list and keeps playing until it's finished. But the length of both Pwhite patterns is infinite, so whichever one is chosen first retains control. Pswitch1 does the coin toss on every event and embeds just one item.
Psym1 and Pnsym1 behave similarly, choosing the name to look up the pattern for each event.
subsection::Related: Conditional patterns
link::Classes/Pif:: supports this kind of structure: If the next value from a Boolean pattern is true, return the next item from pattern A, otherwise take it from pattern B. Another way to write the Pswitch1 example is to use a Boolean test directly on Pwhite, instead of writing a Pfunc for the coin toss. This might be clearer to read. However, this works only when there are two alternatives. Pswitch1 and Psym1 allow any number of choices.
code::
(
TempoClock.default.tempo = 1;
p = Pbind(
// translation: if(0.7.coin) { rrand(-7, 0) } { rrand(7, 14 }
\degree, Pif(Pwhite(0.0, 1.0, inf) < 0.7, Pwhite(-7, 0, inf), Pwhite(7, 14, inf)),
\dur, 0.25
).play;
)
p.stop;
::
We will see in link::Tutorials/A-Practical-Guide/PG_06e_Language_Control:: that Pif can be used on values that were previously calculated in the Pbind. It adds considerably to the intelligence Pbind can manage, when its value streams are aware of other values in the event.
Previous: link::Tutorials/A-Practical-Guide/PG_03_What_Is_Pbind::
Next: link::Tutorials/A-Practical-Guide/PG_05_Math_on_Patterns::
|