Patterns

[Topic]
  Patterns

Patterns are objects that generate data according to pattern specific ordering rules. Patterns can hold any sort of Lisp data and may include nested sub-patterns that define smaller (local) orderings within the surrounding pattern.

#### Pattern Classes

The system provides a number of generic pattern types that can used individually or nested together to form composite patterns:

Table 1. Generic pattern classes.

Pattern | Description
--- | ---
`cycle` | loops over elements in a continuous cycle
`line` | enumerates sequentially and sticks on last element
`palindrome` | generates elements forwards and backwards
`heap` | randomly permutes elements
`random` | selects elements from a weighted distribution
`markov` | returns nth order Markov chains of its elements
`graph` | generates elements from a directed graph
`funcall` | computes elements by function call
`rotation` | returns systematic permutations of its elements
`rewrite` | generates elements by rewrite rules

There are also a few specialized pattern classes that implement more specific, or restricted, functionality:

Table 2. Specialized pattern classes.

Pattern | Description
--- | ---
`copier` | copies and repeats periods of a sub-pattern
`range` | iterates numbers in a range
`join` | merges patterns together into a single stream of data
`transposer` | transposes and optionally inverts and reverses pattern data
`chord` | returns lists of chord data
`pval` | evaluates a pattern value

#### Generic Pattern Initializations

The pattern classes listed in Table 1 all support the following slot initializations:

`:of` data
  Specifies the element or list of elements to generate from the pattern. This data can include sub-patterns. The `:of` initialization has several aliases that parse the associated pattern data into musical information:
  `:notes` data
    Parses data into note names. If data contains notes then octave numbers only need to be added when they change from the previous note.
  `:keynums` data
    Like `:notes` but parses data into key numbers.
  `:hertz` data
    Like `:notes` but parses data into hertz values.
  `:rhythms` data
    Parses logical rhythms into rhythmic values.
  `:amplitudes` data
    Parses logical amplitudes into amplitude values.
`:for` {number | pattern}
  Sets the period length of the pattern to number or pattern of numbers. The period length determines the size (number of elements) in each period, or chunk, of elements that the pattern generates.
`:repeat` number
  Sets an optional repetition limit for the pattern. Once the limit has been reached the pattern will return an end of data marker instead of elements.
`:name` {string | symbol}
  Sets the name of the pattern to string or to the print name of symbol. A named pattern can be recalled as a "motive" inside a surrounding pattern by fetching the object from its name using the #& read macro or the find-object function.
`:returning` function
  An optional function to apply to each element as it is generated from the pattern. The function is passed one argument, the element, and should return the value to substitute in its place as the value returned from the pattern.
`:parsing` function
  An optional function to apply to the data passed to the `:of` initialization when the pattern is created. The function is passed one argument, the element to parse, and should return the value to substitute in its place in the pattern.

Consult the dictionary entries of each pattern for additional keyword initializations specific to each class.

#### Working with Patterns

Use the `new` macro or the generic function `make-instance` to create patterns and the next function to read successive elements from them:

Example 1. Creating patterns and reading their elements.

    (define pat1 (new cycle :keynums '(a4 b c5 d)))

    (next pat1)
    ⇒ 69
    (next pat1)
    ⇒ 71
    (next pat1 2)
    ⇒ (72 74)
    (next pat1 #t)
    ⇒ (69 71 72 74)

    (define (play-pat times knums durs amps tmpo)
      (process repeat times
               for k = (next knums)
               for r = (* (next durs) tmpo)
               for a = (next amps)
               output (new midi :time (now)
                           :keynum k
                           :duration (* r 1.5)
                           :amplitude a)
               wait r))

    (events (play-pat 20 pat1 .125 .3 1) "test.mid")
    ⇒ "test.mid"

The next example demonstrates the use of sub-patterns in pattern building. The pat1 pattern randomly chooses between two sub-patterns: a cycle of white notes and a heap, or "shuffling", of black notes. The pat2 pattern is a rhythmic cycle looping over two elements, the symbol e (eighth note) and a line pattern that generates four sixteenths (s) in succession:

Example 2. Patterns and sub-patterns.

    (define pat1
      (new random :of (list (new cycle :keynums '(a4 b c5 d))
                            (new heap :keynums '(gs4 as cs5 ds)))))

    (define pat2
      (new random :rhythms `(e ,(new line :rhythms 's :for 4))))

    (events (play-pat 60 pat1 pat2 .1 .5) "test.mid")
    ⇒ "test.mid"

Since `make-instance` evaluates its first argument (new does not) the class of the pattern it creates can vary at runtime:

Example 3. Creating patterns programmatically.

    (define (play-pats pats trope reps rate)
      (process with dur = (* rate 2.5)
               repeat reps
               for len = (pick 8 12 16)
               for pat = (make-instance (next pats)
                                        :keynums trope
                                        :for len)
               each k in (next pat #t) as x from 0 by rate
               output (new midi :time (+ (now) x)
                           :keynum k
                           :duration dur)
               wait (* rate len)))

    ;;; a pattern of pattern class names
    (define pcns
      (new random :of '((heap :weight 2) line cycle
                        palindrome rotation))

    (events (play-pats pcns '(a4 b c5 d) 12 .1) "test.mid")
    ⇒ "test.mid"

The `play-pats` process creates patterns by selecting a class from a pattern of classes and then calling `make-instance` on the selected pattern class to create the pattern. The process then iterates over one period of the pattern and outputs a midi note each element.

The preceding three examples all illustrate that the pattern accessor next can read single elements, specified numbers of elements or whole periods of elements from a pattern, and that the period length and the number of elements in a pattern do not have to be the same. The period length can be any non-negative integer (including zero) or a pattern of integers, in which case a new length is chosen from the pattern for each new period. If a sub-pattern ever sets its period length to zero, it "disappears" inside the surrounding pattern until it is selected again. By changing period lengths dynamically even very simple pattern types can produce interesting effects.

In the next example only cycle patterns are used but the sub-patterns vary their lengths according to different cycles. The overall effect is a bit like a "musical mobile" in which the cycles of elements rotate about each other in different orbits.

Example 4. Period length patterns.

    (define pat1
      (new cycle :of (list (new cycle :notes '(a4 b c5 d)
                                :for (new cycle :of '(4 3 2 1 0)))
                           (new cycle :of '(e4 e5 e6 e3)
                                :for (new cycle :of '(0 1 2 3)))
                           (new cycle :of '(f5 ef4) :for 1))))

    (define pat2
      (new random :rhythms `(e ,(new line :rhythms 's :for 4))))

    (events (play-pat 80 pat1 pat2 .4 .75) "test.mid")
    ⇒ "test.mid"

The repeat factor of a pattern establishes a limit on the number of periods that a pattern can generate. Once a pattern reaches this maximum it will return an end of data marker for all subsequent calls to next. By using the eod? and eop? functions, a process can treat the repeat factor and the period length as markers to control musical evolution, without knowing their actual values.

Example 5. Checking for end-of-period and end-of-data conditions.

    (define (play-trope pat trope reps rate amp)
      ;; play trope for reps number of periods and make the first
      ;; note in each period louder than the others.
      (let ((p (make-instance pat :notes trope
                              :for (new random :of '(1 2 3 4 5))
                              :repeat reps)))
        (process with a = amp
                 for n = (next p)
                 until (eod? p)  ; stop at end of data
                 output (new midi :time (now)
                             :keynum n
                             :amplitude a
                             :duration (* rate 2.5))
                 ;; if end of period, make the next note loud
                 when (eop? p) set a = amp else set a = (* amp .6)
                 wait rate)))

    (events (play-trope 'cycle '(a4 b c5 d) 30 .1 .8) "test.mid")
    ⇒ "test.mid"

    (events (list (play-trope 'heap '(a3 b c4 d e f g a) 80 .1 .7)
                  (play-trope 'cycle '(a4 b c5 d e ) 70 .1 .7)
                  (play-trope 'cycle '(a5 b c6 d) 60 .1 .8))
            "test.mid"
            '(0 4 10))
    ⇒ "test.mid"

#### See also:

- Generic pattern classes
- Specialized pattern classes
- `process` [Macro]