CCRMA

Common Music Patterns, Random Processes


Abstractions

Using functions to create meta-instruments...

Using functions as envelope generators...

Common Music Patterns

Introduction to Common Music patterns. See the Common Music Manual for version 2.4.0, or the more complete but in some cases outdated documentation for the 2.3.4 version. Look at the Common Music dictionary and the (old) section on patterns

Pattern Classes

During the class we took a look at the simplest classes or types of patterns. Each class of patterns has a different behavior in how elements are selected:

cycle
circular selection of elements in the pattern
line
go from first to last, repeat last
palindrome
first to last and back
heap
random selection without replacement
random
random selection with replacement

Patterns are objects created with the "new" macro. The following line stores a cyclic pattern (cyclic patterns are the default type of pattern) that contains five elements in the global variable "pat":

  (setf pat (new pattern :of '(1 2 3 4 5)))

We can access elements through the "next" function, which can either return one element at a time or a whole period of selected elements:

  (next pat)
  1
  nil
  (next pat)
  2
  nil

or if we want a whole "period" returned in a list we add an optional argument with the value ":chunk":

  (next pat :chunk)
  (1 2 3 4 5)

by default the period of a pattern is equal to the number of elements it contains. We can create a specific kind of pattern by naming its type when creating it:

  (setf pat (new cycle :of '(1 2 3 4 5)))
  (setf pat (new random :of '(1 2 3 4 5)))

Patterns can be nested to arbitrary depths, the following pattern contains a random sub-pattern as element four:

  (setf pat (new cycle :of (list 1 2 3 (new random :of '(10 20 30)) 5)))
  (next pat :chunk)
  (1 2 3 10 30 20 5)

Note that the included pattern completes its own period before returning control to the enclosing pattern. This behavior can be changed by altering the default period of the pattern using the for keyword:

  (setf pat (new cycle :of (list 1 2 3 (new random :of '(10 20 30) :for 1) 5)))
  (next pat :chunk)
  (1 2 3 10 5)
  (next pat :chunk)
  (1 2 3 30 5)

The period itself could be a pattern!:

  (setf pat (new cycle :of (list 1 2 3 (new random :of '(10 20 30) :for (new heap :of '(1 2))) 5)))
  (next pat :chunk)
  (1 2 3 20 10 5)
  (next pat :chunk)
  (1 2 3 10 5)

If you want to supply a prebuilt list of elements to the new macro just use the of keyword followed by a list of elements:

  (setf elements '(1 2 3 4))
  (setf pat (new heap :of elements :for 3))

Some tools included in Common Music

See the (old documentation for 2.3.4) tools section in the Common Music reference manual for more details. There are some interesting functions that let you translate between different representation of musically interesting magnitudes (pitch/frequency, tempo/seconds, loudness/amplitude).

note
translates to note names from note names or key numbers
keynum
translates to key numbers from note names or frequencies
hertz
translates to frequencies either note numbers or key numbers

The last one is particularly interesting for controlling CLM instruments as most of them can only deal with frequency and not the most common note numbers or key numbers that represent the popular twelve tone tempered scale (Common Music can also work with other scales...). For example, we could be controlling our fm-violin using a heap pattern of note numbers that we translate into frequencies:

(with-sound()
  (let* ((notes (new heap :of '(64 66 68 61 60))))
    (loop
      for time from 0 by 0.1 repeat 10 do
      (fm-violin time 0.1 (hertz (next notes)) 0.1))))

Common Music patterns can be told to use a function to parse the elements selected before returning them by using the "parsing" keyword:

Functions also exist that deal with rhythmic time and logical amplitudes

CLM instruments and Common Music objects

When you compile a CLM instrument Common Music also creates a Common Music class that knows the parameter structure of the instrument itself. So, instruments can be called as functions within a with-sound macro or can be instantiated from the corresponding Common Music instrument class and then rendered from within the context of Common Music. See Reading and Writing Events in the IO section of the Common Music reference manual.

In the following example we create a single fm-violin object and render it using the "events" Common Music function:

(setf a-note (new fm-violin startime 0 dur 1 frequency 440 amplitude 0.1))
(events a-note "/zap/test.snd")
Use "(dac "/zap/test.snd") to play the resulting file.

Compare it with doing the same thing in pure clm:

(with-sound()(fm-violin 0 1 440 0.1))

The new macro creates a new fm-violin object with the named values for its parameters. The events function takes an object or a container of objects and renders it to the specified destination (in this case the "/zap/test.snd" soundfile). Note that events does not play the resulting file, you will have to use (dac "/zap/test.snd") to play it.

Both ways of sounding clm notes are equivalent. The Common Music object oriented abstraction will be more useful latter on. We could obviously create a container full of fm-violin objects and render it with events:

(setf some-notes
      (loop repeat 8
	with notes = (new heap :of '(64 65 69 54))
	with rhythms = (new heap :of '(q q e e.. s s))
	with amplitudes = (new random :of '(0.1 0.01 0.2))
	for time from 0 by (rhythm 'e)
	collect (new fm-violin
		     startime time
		     dur (rhythm (next rhythms))
		     frequency (hertz (next notes))
		     amplitude (next amplitudes))))
(events some-notes "/zap/test.snd")

The Common Music object representation of CLM instruments has a potential advantage when dealing with algorithmic composition. Instantiated CLM instrument objects are persistent and can be manipulated in several ways before being rendered. For example we could loop through a container full of objects and change one of the parameters before sending the objects to events to be rendered. In the following (nonsensical, just to illustrate the point) example we create a contained (list) full of fm-violin notes with time = 0 (that is, a gigantic "chord" if rendered as is). Then we iterate through the list a assign starting times to the notes:

(setf some-notes
      (loop repeat 8
	with notes = (new heap :of '(64 65 69 54))
	with rhythms = (new heap :of '(q q e e.. s s))
	with amplitudes = (new random :of '(0.1 0.01 0.2))
	collect (new fm-violin
		     ;; we don't define a time at this point
		     ;; we just set it to 0
		     startime 0
		     dur (rhythm (next rhythms))
		     frequency (hertz (next notes))
		     amplitude (next amplitudes))))

;; "in" iterates in a list
(loop for note in some-notes
  ;; the whole thing starts at 0
  with time = 0
  ;; create a pattern for start rhythms
  with start = (new random :of '(q q e e s s s s))
  do
  (sv note startime time)
  (incf time (rhythm (next start))))

(events some-notes "/zap/test.snd")

"sv" sets a particular parameter of a Common Music object to a value.


Probability distributions

Probability is the likehood of occurrence of a given event, ratio of number of occurrences of that event to the total number of results of the random process. A probability distribution is a table that shows the likehood of occurrence of one of more events, where the probability of occurrence of a given event is expressed as a value between 0 and 1.

[continuous vs discrete distributions]

Random numbers... as normally generated in a computer are not really really random. They are the result of executing an algorithm, the output values can be considered statistically random if the algorithm is good enough. Normally the random number generator is seeded by a number, after that the numbers generated are predictable (each time we seed the generator with the same number it produces the same sequence of "random" numbers).

In common lisp we can generate random floating point values using the (random xxx) function, where xxx is a floating point number. If we need integers (for example to generate midi note numbers) then the argument to random should be an integer.

[Distributions: uniform, linear, triangular, exponential...]

The example file probability.lisp has some examples of probability distributions (Note: you'll have to first compile and load plot.lisp to be able to plot the distributions)

See some useful functions in Common Music: drunk, between,

State Machines

[a definition]

A state machine remembers a "state" (a memory of where it is in its internal process) and depending on whar state it is in, it calculates outputs that are determined by the current inputs and then transitions to a different (or the same) state. We could specify a state machine by a connected graph. States are represented by circles. Directed arrows specify the transitions between states. Each transition is determined by the "current state" (that is, the circle it originates in) and by the input conditions. As a side effect of the state transition the state machine defines new output values.

[Mealy: transition -> output; Moore: state -> output]

A very simple state machine could be defined by the following lisp code:

(defparameter h-state t)

(defun harmonize (input)
  (if h-state
      ;; up by a fifth
      (progn
	(setf h-state nil)
	(+ input 7))
    ;; up by a fourth
    (progn
      (setf h-state t)
      (+ input 5))))

h-state is a global variable that remembers the current state of the state machine. It can have two values, t or nil (ie: this state machine has only two state). The function definition is simple, depending on h-state is executes two branches of code, each one calculates and returns an output value based on the input to the state machine and the current state. It then transitions to the other state.

(with-sound()
  (loop
    repeat 10
    for time from 0 by 0.2
    for note = 60 then (harmonize note)
    do
    (fm-violin time 0.16 (hertz (if (> note 128) 128 note)) 0.1)))

See statemachine.lisp for ready to run examples...

Each state within the state machine has to define what to do with the outputs and how to transition to the next state. So far we have only seen a completely deterministic behavior in our state machine.

What if some of the decisions were taken by stochastic (random) processes? The output of our state machine is no longer completely predictable. Those stochastic decisions could influence the transition between states of the generation of outputs from the current inputs to the state machine.

  • State Machines
    • Deterministic
    • Stochastic

[Markov chains: probability for transitions between states]

see the class lisp transcript for examples on how to define and use markov chains in our Lisp environment


©1998, 2001-2003 Fernando Lopez-Lezcano. All Rights Reserved.
nando@ccrma.stanford.edu