ï~~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
Top of page Top of page