Skip to content

Latest commit

 

History

History
128 lines (78 loc) · 7.32 KB

tutorial.org

File metadata and controls

128 lines (78 loc) · 7.32 KB

cl-patterns Tutorial

Introduction

cl-patterns is a library for creating and manipulating patterns. “Patterns” in this case refers to a set of functions and classes provided by the library that produce numeric or compound output. The term “pattern” can also refer to the structures built out of these functions.

Though it can be used for any purpose, cl-patterns is primarily written with music composition and performance in mind. In this regard, it can be thought of as a sequencing library, however unlike most step sequencers or piano rolls, cl-patterns offers much more flexibility, and can be used to define far more complex and dynamic behaviors.

cl-patterns is based on and inspired by SuperCollider’s patterns system; however, there have been many changes and improvements to it such that experienced SuperCollider users may need to re-learn some things, and perhaps break some old habits.

If you’re familiar with SuperCollider’s patterns, you may find it useful to look at sc-differences.org instead. This document is meant for people who are not familiar with SuperCollider’s patterns system at all, and need a general overview of what it is and how to use it.

Basics

As mentioned above, this library is based around the idea of “patterns”, which are basically functions that produce a sequence of related or unrelated outputs.

Outputs typically take the form of numbers or “events”. An event is similar to a hash table or dictionary, but specialized for cl-patterns’ purposes with specific get/set methods that wrap extra functionality. Events are usually used to represent actions for a music synthesizer to take, such as playing a note or changing a parameter.

The simplest pattern class provided by cl-patterns is probably pseq, which accepts a list and a repeat count as its arguments and returns successive elements from the list as its outputs. To create an instance of pseq, we would write something like the following:

(pseq '(1 2 3) 2)

This creates a pseq with (1 2 3) as its list, and 2 as its repeat count. After you type that into the REPL, you should see the pseq instance returned. To get the values from a pattern, we can use functions like next-n:

(next-n (pseq '(1 2 3) 2) 7) ;=> (1 2 3 1 2 3 NIL)

next-n takes a source to pull values from (in this case, our pseq) and an integer representing the number of results we want from it. As you can see, it returns a list of the results from the pseq: the list repeated twice, and then a NIL, which is the value returned by patterns after they’ve finished their output.

When called like this, next-n is doing a lot of things “under the hood”. Most obviously, what it’s doing is calling next repeatedly. We can also call next ourselves like so:

(next (pseq '(1 2 3) 2)) ;=> 1

And when we do so, we get only one output, as expected. In this case, it’s 1, the first element of the pseq’s list. However, if we evaluate the same line again, we’ll keep getting 1. That means next-n must be doing more than just calling next if it’s able to get more results from the pseq. And indeed; next-n also calls as-pstream “under the hood”. We can do the same:

(as-pstream (pseq '(1 2 3) 2))

When we run this line of code, we get a pstream object instead of a pattern. Now, if we call next repeatedly on this pstream, we’ll get all of the outputs:

(defparameter pstr (as-pstream (pseq '(1 2 3) 2)))

(next pstr) ;=> 1

(next pstr) ;=> 2

(next pstr) ;=> 3

;; ...and so on.

This is because patterns are basically “templates” that define behavior; they don’t keep track of execution state. Only pstreams keep track of execution state.

Events

cl-patterns also has patterns that output events, as described above. pbind is the primary of these, as it’s used to define most note, rest, and parameter change events. Most of the time, when you’re using cl-patterns with an audio server, playing a pbind will trigger sounds to occur.

More Patterns

There are, of course, many other types of patterns aside from pseq and pbind. You can use the (all-patterns) function to get a list of all of the pattern classes defined by the library. All patterns have documentation strings which describe what they do as well as provide examples and lists of related patterns or functions. For an overview of the patterns included with cl-patterns, refer to patterns.org.

Backends

Since cl-patterns is just a sequencing library, it does not produce any audio on its own. Instead, cl-patterns utilizes one or more backends for this task.

The backend with the most popularity, development, and testing behind it is the supercollider backend, which communicates with the SuperCollider sound server via the cl-collider library. Aside from it, there is also a backend for interacting with Incudine, and another for sending MIDI via the ALSA system on Linux.

Starting a backend

To make use of a backend, do the following:

  1. Load its sub-system. Usually the name of the sub-system is cl-patterns/backend-name. So for the supercollider backend:
(ql:quickload :cl-patterns/supercollider)

Or for ALSA MIDI:

(ql:quickload :cl-patterns/alsa-midi)
  1. Start the backend. This can be done with the backend-start function, optionally with additional keyword arguments to adjust parameters of the backend.

    For example, the supercollider backend can be started like so:

(backend-start 'supercollider)
  1. Actually, that’s it!

Using backends

Once a backend is loaded and started, it can be used by play-ing events or patterns:

(play (event :instrument :synth-name :midinote 64 :amp 0.1))

(play (pbind :instrument :synth-name :degree (pseries 0 1 8) :dur 1/4))

If only one backend is enabled and started, it will be the one used. You can also enable more than one, though, in which case the system will use the backend that was started first as the default one.

To see the currently enabled backends:

(enabled-backends)

The first one listed is the default.

You can specify that an event or pattern should be played with a different backend using the :backend key. For example, to trigger a MIDI note via the ALSA MIDI backend, you can do something like the following:

(play (event :backend :alsa-midi :instrument 0))

Different backends may use event keys in different ways, or may support keys that others don’t. For example, the supercollider backend uses the :instrument key to specify the name of the synth to play the note with.

Contrast that with the alsa-midi backend, which uses :instrument to specify the program number to play the note with. When :instrument is specified in an event that alsa-midi plays, it will send a MIDI program change event on the channel before sending the MIDI note on event.