ï~~combined pattern with period of the lowest common multiple of that of the constituent patterns.
Combining patterns by repetition is straightforward, and
denoted by square brackets, where constituent parts are separated by commas:
draw "[black blue green, orange red]"
Combining by padding each part with rests is denoted curly
brackets, and inspired by the Bol Processor [2]. In this example the first part is padded with one rest every step, and
the second with two rests:
draw "{black blue green, orange red}"
Polymetries may be embedded to any depth (note the use of
a tilde to denote a rest):
draw "[ {black ~ grey, orange}, red green] "
There are other ways of constructing patterns, for example
the s ine1 function produces a sine cycle of floating point
numbers with a given period, here rendered as grey values
with the drawGray function:
drawGray $ sinel 16
3.2. Pattern transformation
When the underlying pattern representation is a list, a pattern transformer must operate directly on sequences of
events. For example, we might rotate a pattern one step
forward by popping from the end of the list, and unshifting/consing the result to the head of the list. In Petrol,
because a pattern is a function from time to events, a transformer may manipulate time values as well as events. Accordingly the Petrol function rotL for rotating a pattern to
the left is defined as:
rotL p n =-Pattern (It - at p (t + n)) (period p)
Rotating to the right is simply defined as the inverse:
rotR pn - rotL p (0 - n)
We won't go into the implementation details of all the
pattern transformers here, suffice to say that they are all implemented as composable behaviours. The reader may refer
to the source code for further details.
The every function allows transformations to only be
applied every n cycles. For example, to rotate a pattern by a
single step every third repetition:
draw $ every 3 ('rotR' 1) "black grey red"
The Pattern type is defined as an Applicative Functor, allowing a function to be applied to every element of a pattern
using the <$> functor map operator. For example, we may
add some blue to a whole pattern by mapping the blend
function (from the Haskell Colour library) over its elements:
draw $ blend 0.5 blue <$> p
where p every 3 ('rotR' 1) "black grey red"
We can also apply the functor map conditionally, for example to transpose every third cycle:
drawGray $ every 3 ((+ 0.6) <$>) "0.2 0.3 0 0.4"
The Haskell Applicative Functor syntax also allows a new
pattern to be composed by applying a function to combinations of values from other patterns. For example, the following gives a polyrhythmic lightening and darkening effect, by
blending values from two patterns:
draw $
(blend 0.5) <$> "red blue" <*> "white white black"
The Petrol onset s function filters out elements that do not
begin a phrase. Here we manipulate the onsets of a pattern
(blending them with red), before combining them back with
the original pattern.
draw $ combine [blend 0.5 red <$> onsets p, p]
where p "blue orange ~ ~ [green, pink] red ~"
The onsets function is particularly useful in cross-domain
patterning, for example taking a pattern of notes and accentuating phrase onsets by making a time offset and/or velocity
pattern from it.
4. MUSICAL APPLICATION
The colour pattern examples in the previous section should
have given an impression of Petrol usage, however we must
now turn the discussion back to music. In improvised music
performance, Petrol patterns are used to control a variety of
parameters.
A central time server controls tempo across multiple instances of Petrol, controllable via a bpm pattern. For example the following decreases the tempo from 120 to 60 bpm
over each 16 beat cycle:
bpm $ tween 120 60 16
Besides controlling bpm, patterns are used to send Open
Sound Control (OSC) messages [5] to synthesisers. A pattern may be defined and redefined for each synthesiser parameter while the music plays. A pattern offset specifies
333